Mark Underwood / Mbed OS Internal_DataLogger_NoUSB_MAX32625PICO

Dependencies:   max32625pico CmdLine

DataLogger_Internal.cpp

Committer:
whismanoid
Date:
2021-05-12
Revision:
41:13a6a097502c
Parent:
40:330b28116178
Child:
42:89607d5d4e6d

File content as of revision 41:13a6a097502c:

// /*******************************************************************************
// * Copyright (C) 2021 Maxim Integrated Products, Inc., All Rights Reserved.
// *
// * Permission is hereby granted, free of charge, to any person obtaining a
// * copy of this software and associated documentation files (the "Software"),
// * to deal in the Software without restriction, including without limitation
// * the rights to use, copy, modify, merge, publish, distribute, sublicense,
// * and/or sell copies of the Software, and to permit persons to whom the
// * Software is furnished to do so, subject to the following conditions:
// *
// * The above copyright notice and this permission notice shall be included
// * in all copies or substantial portions of the Software.
// *
// * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
// * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// * OTHER DEALINGS IN THE SOFTWARE.
// *
// * Except as contained in this notice, the name of Maxim Integrated
// * Products, Inc. shall not be used except as stated in the Maxim Integrated
// * Products, Inc. Branding Policy.
// *
// * The mere transfer of this software does not imply any licenses
// * of trade secrets, proprietary technology, copyrights, patents,
// * trademarks, maskwork rights, or any other form of intellectual
// * property whatsoever. Maxim Integrated Products, Inc. retains all
// * ownership rights.
// *******************************************************************************
// */
// *******************************************************************************
//          COM port settings are 9600 baud 8N1
// *******************************************************************************
// Support custom target MAX40108DEMOP2U9 based on MAX32625PICO but MAX32625_NO_BOOT
// file custom_targets.json:
// {
//     "MAX40108DEMOP2U9": {
//         "inherits": ["MAX32625_BASE"],
//         "macros_remove": [],
//         "macros_add": ["MAX32625_NO_BOOT"]
//      }
// }
// files copied from mbed-os\targets\TARGET_Maxim\TARGET_MAX32625\TARGET_MAX32625PICO
// file TARGET_MAX40108DEMOP2U9\PeripheralNames.h -- copied from TARGET_MAX32625PICO
// file TARGET_MAX40108DEMOP2U9\PinNames.h -- copied from TARGET_MAX32625PICO
// files copied from mbed-os\targets\TARGET_Maxim\TARGET_MAX32625\device\___\TARGET_MAX32625_NO_BOOT
// file TARGET_MAX40108DEMOP2U9\device\TOOLCHAIN_ARM_STD\MAX32625.sct
// file TARGET_MAX40108DEMOP2U9\device\TOOLCHAIN_GCC_ARM\max32625.ld
// file TARGET_MAX40108DEMOP2U9\device\TOOLCHAIN_IAR\MAX32625.icf
// file mbed_app.json:
// {
//     "config": {
//     },
//     "macros": [ 
//         "MAX40108_DEMO=9"
//     ],
//     "target_overrides": {
//     }
// }
// *******************************************************************************
// Validating project global defines from mbed_app.json "macros": []
#ifndef MAX40108_DEMO
#warning "MAX40108_DEMO not defined, missing mbed_app.json"
#else // #ifndef MAX40108_DEMO
#warning "Note: MAX40108_DEMO is defined, which is expected"
#if (MAX40108_DEMO)==(9)
#warning "Note: MAX40108_DEMO is defined with the expected value of 9"
#elif (MAX40108_DEMO)==(5)
#warning "Note: MAX40108_DEMO is defined with the wrong value 5"
#else
#warning "Note: MAX40108_DEMO is defined, but with an unsupported value"
#endif
#endif // #ifndef MAX40108_DEMO
// *******************************************************************************
#ifndef MAX40108_DEMO
// #define MAX40108_DEMO 5 for U5, or #define MAX40108_DEMO 9 for U9 in banner
#define MAX40108_DEMO 9
#define HAS_DAPLINK_SERIAL 1
#endif // MAX40108_DEMO
#ifdef BOARD_SERIAL_NUMBER
// data unique to certain boards based on serial number
# if          (BOARD_SERIAL_NUMBER) == 0
    #warning "(BOARD_SERIAL_NUMBER) == 0"
    //
# elif        (BOARD_SERIAL_NUMBER) == 1
    #warning "(BOARD_SERIAL_NUMBER) == 1"
    //
# elif        (BOARD_SERIAL_NUMBER) == 2
    #warning "(BOARD_SERIAL_NUMBER) == 2"
    //
# elif        (BOARD_SERIAL_NUMBER) == 3
    #warning "(BOARD_SERIAL_NUMBER) == 3"
    //
# elif        (BOARD_SERIAL_NUMBER) == 4
    #warning "(BOARD_SERIAL_NUMBER) == 4"
    //
# elif        (BOARD_SERIAL_NUMBER) == 5
    #warning "(BOARD_SERIAL_NUMBER) == 5"
    //
# elif        (BOARD_SERIAL_NUMBER) == 6
    #warning "(BOARD_SERIAL_NUMBER) == 6"
    //
# else
    #warning "BOARD_SERIAL_NUMBER defined but not recognized"
# endif
#else // BOARD_SERIAL_NUMBER data unique to certain boards based on serial number
    #warning "BOARD_SERIAL_NUMBER not defined; using default values"
//
#endif // BOARD_SERIAL_NUMBER data unique to certain boards based on serial number
//---------- CODE GENERATOR: DataLogHelloCppCodeList
// CODE GENERATOR: example code includes

// example code includes
// standard include for target platform -- Platform_Include_Boilerplate
#include "mbed.h"
// Platforms:
//   - MAX32625MBED
//      - supports mbed-os-5.11, requires USBDevice library
//      - add https://developer.mbed.org/teams/MaximIntegrated/code/USBDevice/
//      - remove max32630fthr library (if present)
//      - remove MAX32620FTHR library (if present)
//   - MAX32600MBED
//      - Please note the last supported version is Mbed OS 6.3.
//      - remove max32630fthr library (if present)
//      - remove MAX32620FTHR library (if present)
//      - Windows 10 note:  Don't connect HDK until you are ready to load new firmware into the board.
//   - NUCLEO_F446RE
//      - remove USBDevice library
//      - remove max32630fthr library (if present)
//      - remove MAX32620FTHR library (if present)
//   - NUCLEO_F401RE
//      - remove USBDevice library
//      - remove max32630fthr library (if present)
//      - remove MAX32620FTHR library (if present)
//   - MAX32630FTHR
//      - #include "max32630fthr.h"
//      - add http://developer.mbed.org/teams/MaximIntegrated/code/max32630fthr/
//      - remove MAX32620FTHR library (if present)
//   - MAX32620FTHR
//      - #include "MAX32620FTHR.h"
//      - remove max32630fthr library (if present)
//      - add https://os.mbed.com/teams/MaximIntegrated/code/MAX32620FTHR/
//      - not tested yet
//   - MAX32625PICO
//      - #include "max32625pico.h"
//      - add https://os.mbed.com/users/switches/code/max32625pico/
//      - remove max32630fthr library (if present)
//      - remove MAX32620FTHR library (if present)
//      - not tested yet
//      - see https://os.mbed.com/users/switches/code/max32625pico/
//      - see https://os.mbed.com/users/switches/code/PICO_board_demo/
//      - see https://os.mbed.com/users/switches/code/PICO_USB_I2C_SPI/
//      - see https://os.mbed.com/users/switches/code/SerialInterface/
//      - Note: To load the MAX32625PICO firmware, hold the button while
//        connecting the USB cable, then copy firmware bin file 
//        to the MAINTENANCE drive.
//      - see https://os.mbed.com/platforms/MAX32625PICO/
//      - see https://os.mbed.com/teams/MaximIntegrated/wiki/MAX32625PICO-Firmware-Updates
//      - update from mbed-os-5.11.5 to mbed-os-5.15 to support deep sleep
//      - cd mbed-os ; mbed update mbed-os-5.15.7 ; cd .. ; mbed remove USBDevice ; mbed sync
//      - (Internal_DataLogger_NoUSB_MAX32625PICO does not use USBDevice library anyway)
//
// end Platform_Include_Boilerplate

//--------------------------------------------------
// Option to use SPI connected ADC
#ifndef SPI_ADC_DeviceName
#define SPI_ADC_DeviceName MAX11410
#undef SPI_ADC_DeviceName
#endif
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
#include "MAX11410.h"
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC

#if defined(TARGET)
// TARGET_NAME macros from targets/TARGET_Maxim/TARGET_MAX32625/device/mxc_device.h
// Create a string definition for the TARGET
#define STRING_ARG(arg) #arg
#define STRING_NAME(name) STRING_ARG(name)
#define TARGET_NAME STRING_NAME(TARGET)
#elif defined(TARGET_MAX32600)
#define TARGET_NAME "MAX32600"
#elif defined(TARGET_LPC1768)
#define TARGET_NAME "LPC1768"
#elif defined(TARGET_NUCLEO_F446RE)
#define TARGET_NAME "NUCLEO_F446RE"
#elif defined(TARGET_NUCLEO_F401RE)
#define TARGET_NAME "NUCLEO_F401RE"
#else
#error TARGET NOT DEFINED
#endif
#if defined(TARGET_MAX32630)
//--------------------------------------------------
// TARGET=MAX32630FTHR ARM Cortex-M4F 96MHz 2048kB Flash 512kB SRAM
//             +-------------[microUSB]-------------+
//             | J1         MAX32630FTHR        J2  |
//      ______ | [ ] RST                    GND [ ] |
//      ______ | [ ] 3V3                    BAT+[ ] |
//      ______ | [ ] 1V8                  reset SW1 |
//      ______ | [ ] GND       J4               J3  |
// analogIn0/4 | [a] AIN_0 1.2Vfs     (bat) SYS [ ] | switched BAT+
// analogIn1/5 | [a] AIN_1 1.2Vfs           PWR [ ] | external pwr btn
// analogIn2   | [a] AIN_2 1.2Vfs      +5V VBUS [ ] | USB +5V power
// analogIn3   | [a] AIN_3 1.2Vfs   1-WIRE P4_0 [d] | D0 dig9
//  (I2C2.SDA) | [d] P5_7  SDA2        SRN P5_6 [d] | D1 dig8
//  (I2C2.SCL) | [d] P6_0  SCL2      SDIO3 P5_5 [d] | D2 dig7
//    D13/SCLK | [s] P5_0  SCLK      SDIO2 P5_4 [d] | D3 dig6
//    D11/MOSI | [s] P5_1  MOSI       SSEL P5_3 [d] | D4 dig5
//    D12/MISO | [s] P5_2  MISO        RTS P3_3 [d] | D5 dig4
//    D10/CS   | [s] P3_0  RX          CTS P3_2 [d] | D6 dig3
//    D9  dig0 | [d] P3_1  TX          SCL P3_5 [d] | D7 dig2
//      ______ | [ ] GND               SDA P3_4 [d] | D8 dig1
//             |                                    |
//             | XIP Flash      MAX14690N           |
//             | XIP_SCLK P1_0  SDA2 P5_7           |
//             | XIP_MOSI P1_1  SCL2 P6_0           |
//             | XIP_MISO P1_2  PMIC_INIT P3_7      |
//             | XIP_SSEL P1_3  MPC P2_7            |
//             | XIP_DIO2 P1_4  MON AIN_0           |
//             | XIP_DIO3 P1_5                      |
//             |                                    |
//             | PAN1326B     MicroSD        LED    |
//             | BT_RX  P0_0  SD_SCLK P0_4   r P2_4 |
//             | BT_TX  P0_1  SD_MOSI P0_5   g P2_5 |
//             | BT_CTS P0_2  SD_MISO P0_6   b P2_6 |
//             | BT_RTS P0_3  SD_SSEL P0_7          |
//             | BT_RST P1_6  DETECT  P2_2          |
//             | BT_CLK P1_7               SW2 P2_3 |
//             +------------------------------------+
// MAX32630FTHR board has MAX14690 PMIC on I2C bus (P5_7 SDA, P6_0 SCL) at slave address 0101_000r 0x50 (or 0x28 for 7 MSbit address).
// MAX32630FTHR board has BMI160 accelerometer on I2C bus (P5_7 SDA, P6_0 SCL) at slave address 1101_000r 0xD0 (or 0x68 for 7 MSbit address).
// AIN_0 = AIN0 pin       fullscale is 1.2V
// AIN_1 = AIN1 pin       fullscale is 1.2V
// AIN_2 = AIN2 pin       fullscale is 1.2V
// AIN_3 = AIN3 pin       fullscale is 1.2V
// AIN_4 = AIN0 / 5.0     fullscale is 6.0V
// AIN_5 = AIN1 / 5.0     fullscale is 6.0V
// AIN_6 = VDDB / 4.0     fullscale is 4.8V
// AIN_7 = VDD18          fullscale is 1.2V
// AIN_8 = VDD12          fullscale is 1.2V
// AIN_9 = VRTC / 2.0     fullscale is 2.4V
// AIN_10 = x undefined?
// AIN_11 = VDDIO / 4.0   fullscale is 4.8V
// AIN_12 = VDDIOH / 4.0  fullscale is 4.8V
//
    #include "max32630fthr.h"
MAX32630FTHR pegasus(MAX32630FTHR::VIO_3V3);
#define analogIn4_IS_HIGH_RANGE_OF_analogIn0 1
// MAX32630FTHR board supports only internal VREF = 1.200V at bypass capacitor C15
const float ADC_FULL_SCALE_VOLTAGE = 1.200;
// Arduino connector
#ifndef A0
#define A0 AIN_0
#endif
#ifndef A1
#define A1 AIN_1
#endif
#ifndef A2
#define A2 AIN_2
#endif
#ifndef A3
#define A3 AIN_3
#endif
#ifndef D0
#define D0 P4_0
#endif
#ifndef D1
#define D1 P5_6
#endif
#ifndef D2
#define D2 P5_5
#endif
#ifndef D3
#define D3 P5_4
#endif
#ifndef D4
#define D4 P5_3
#endif
#ifndef D5
#define D5 P3_3
#endif
#ifndef D6
#define D6 P3_2
#endif
#ifndef D7
#define D7 P3_5
#endif
#ifndef D8
#define D8 P3_4
#endif
#ifndef D9
#define D9 P3_1
#endif
#ifndef D10
#define D10 P3_0
#endif
#ifndef D11
#define D11 P5_1
#endif
#ifndef D12
#define D12 P5_2
#endif
#ifndef D13
#define D13 P5_0
#endif
//--------------------------------------------------
#elif defined(TARGET_MAX32625MBED)
//--------------------------------------------------
// TARGET=MAX32625MBED ARM Cortex-M4F 96MHz 512kB Flash 160kB SRAM
//             +-------------------------------------+
//             |   MAX32625MBED Arduino UNO header   |
//             |                                     |
//             |                           A5/SCL[ ] |   P1_7 dig15
//             |                           A4/SDA[ ] |   P1_6 dig14
//             |                         AREF=N/C[ ] |
//             |                              GND[ ] |
//             | [ ]N/C                    SCK/13[ ] |   P1_0 dig13
//             | [ ]IOREF=3V3             MISO/12[ ] |   P1_2 dig12
//             | [ ]RST                   MOSI/11[ ]~|   P1_1 dig11
//             | [ ]3V3                     CS/10[ ]~|   P1_3 dig10
//             | [ ]5V0                         9[ ]~|   P1_5 dig9
//             | [ ]GND                         8[ ] |   P1_4 dig8
//             | [ ]GND                              |
//             | [ ]Vin                         7[ ] |   P0_7 dig7
//             |                                6[ ]~|   P0_6 dig6
//       AIN_0 | [ ]A0                          5[ ]~|   P0_5 dig5
//       AIN_1 | [ ]A1                          4[ ] |   P0_4 dig4
//       AIN_2 | [ ]A2                     INT1/3[ ]~|   P0_3 dig3
//       AIN_3 | [ ]A3                     INT0/2[ ] |   P0_2 dig2
// dig16  P3_4 | [ ]A4/SDA  RST SCK MISO     TX>1[ ] |   P0_1 dig1
// dig17  P3_5 | [ ]A5/SCL  [ ] [ ] [ ]      RX<0[ ] |   P0_0 dig0
//             |            [ ] [ ] [ ]              |
//             |  UNO_R3    GND MOSI 5V  ____________/
//              \_______________________/
//
//             +------------------------+
//             |                        |
//             |  MicroSD        LED    |
//             |  SD_SCLK P2_4   r P3_0 |
//             |  SD_MOSI P2_5   g P3_1 |
//             |  SD_MISO P2_6   b P3_2 |
//             |  SD_SSEL P2_7   y P3_3 |
//             |                        |
//             |  DAPLINK      BUTTONS  |
//             |  TX P2_1      SW3 P2_3 |
//             |  RX P2_0      SW2 P2_2 |
//             +------------------------+
//
// AIN_0 = AIN0 pin       fullscale is 1.2V
// AIN_1 = AIN1 pin       fullscale is 1.2V
// AIN_2 = AIN2 pin       fullscale is 1.2V
// AIN_3 = AIN3 pin       fullscale is 1.2V
// AIN_4 = AIN0 / 5.0     fullscale is 6.0V
// AIN_5 = AIN1 / 5.0     fullscale is 6.0V
// AIN_6 = VDDB / 4.0     fullscale is 4.8V
// AIN_7 = VDD18          fullscale is 1.2V
// AIN_8 = VDD12          fullscale is 1.2V
// AIN_9 = VRTC / 2.0     fullscale is 2.4V
// AIN_10 = x undefined?
// AIN_11 = VDDIO / 4.0   fullscale is 4.8V
// AIN_12 = VDDIOH / 4.0  fullscale is 4.8V
//
//#include "max32625mbed.h" // ?
//MAX32625MBED mbed(MAX32625MBED::VIO_3V3); // ?
#define analogIn4_IS_HIGH_RANGE_OF_analogIn0 1
// MAX32630FTHR board supports only internal VREF = 1.200V at bypass capacitor C15
const float ADC_FULL_SCALE_VOLTAGE = 1.200;     // TODO: ADC_FULL_SCALE_VOLTAGE Pico?
// Arduino connector
#ifndef A0
#define A0 AIN_0
#endif
#ifndef A1
#define A1 AIN_1
#endif
#ifndef A2
#define A2 AIN_2
#endif
#ifndef A3
#define A3 AIN_3
#endif
#ifndef D0
#define D0 P0_0
#endif
#ifndef D1
#define D1 P0_1
#endif
#ifndef D2
#define D2 P0_2
#endif
#ifndef D3
#define D3 P0_3
#endif
#ifndef D4
#define D4 P0_4
#endif
#ifndef D5
#define D5 P0_5
#endif
#ifndef D6
#define D6 P0_6
#endif
#ifndef D7
#define D7 P0_7
#endif
#ifndef D8
#define D8 P1_4
#endif
#ifndef D9
#define D9 P1_5
#endif
#ifndef D10
#define D10 P1_3
#endif
#ifndef D11
#define D11 P1_1
#endif
#ifndef D12
#define D12 P1_2
#endif
#ifndef D13
#define D13 P1_0
#endif
//--------------------------------------------------
#elif defined(TARGET_MAX32600)
// target MAX32600
//
#define analogIn4_IS_HIGH_RANGE_OF_analogIn0 0
const float ADC_FULL_SCALE_VOLTAGE = 1.500;
//
//--------------------------------------------------
#elif defined(TARGET_MAX32620FTHR)
#warning "TARGET_MAX32620FTHR not previously tested; need to define pins..."
#include "MAX32620FTHR.h"
// Initialize I/O voltages on MAX32620FTHR board
MAX32620FTHR fthr(MAX32620FTHR::VIO_3V3);
//#define USE_LEDS 0 ?
#define analogIn4_IS_HIGH_RANGE_OF_analogIn0 1
#warning "TARGET_MAX32620FTHR not previously tested; need to verify ADC_FULL_SCALE_VOLTAGE..."
const float ADC_FULL_SCALE_VOLTAGE = 1.200;
//
//--------------------------------------------------
#elif defined(TARGET_MAX32625PICO) || defined(TARGET_MAX40108DEMOP2U9)
// #warning "TARGET_MAX32625PICO not previously tested; need to define pins..."
#include "max32625pico.h"
// configure MAX32625PICO VDDIOH mode, and I/O voltages for DIP pins and SWD pins
MAX32625PICO pico(
    // Select source of higher-voltage logic high supply VDDIOH
    // vddioh_mode_t iohMode
    //~ MAX32625PICO::IOH_OFF, // No connections to VDDIOH
    //~ MAX32625PICO::IOH_DIP_IN, // VDDIOH input from DIP pin 1 (AIN0)
    //~ MAX32625PICO::IOH_SWD_IN, // VDDIOH input from SWD pin 1
    MAX32625PICO::IOH_3V3, // VDDIOH = 3.3V from local supply
    //~ MAX32625PICO::IOH_DIP_OUT, // VDDIOH = 3.3V output to DIP pin 1
    //~ MAX32625PICO::IOH_SWD_OUT, // VDDIOH = 3.3V output to SWD pin 1
    //
    // Digital I/O pin logic high voltage 1.8V or 3.3V
    // vio_t dipVio = MAX32625PICO::VIO_1V8 or MAX32625PICO::VIO_IOH
    MAX32625PICO::VIO_1V8, // 1.8V IO (local)
    //~ MAX32625PICO::VIO_IOH, // Use VDDIOH (from DIP pin 1, or SWD pin1, or local 3.3V)
    //
    // Software Debug logic high voltage (normally use VIO_IOH)
    // vio_t swdVio
    //~ MAX32625PICO::VIO_1V8  // 1.8V IO (local)
    MAX32625PICO::VIO_IOH  // Use VDDIOH (from DIP pin 1, or SWD pin1, or local 3.3V)
    );
//#define USE_LEDS 0 ?
#define analogIn4_IS_HIGH_RANGE_OF_analogIn0 1
// #warning "TARGET_MAX32625PICO not previously tested; need to verify ADC_FULL_SCALE_VOLTAGE..."
const float ADC_FULL_SCALE_VOLTAGE = 1.200;
//
//--------------------------------------------------
#elif defined(TARGET_NUCLEO_F446RE) || defined(TARGET_NUCLEO_F401RE)
// TODO1: target NUCLEO_F446RE
//
// USER_BUTTON PC13
// LED1 is shared with SPI_SCK on NUCLEO_F446RE PA_5, so don't use LED1.
#define USE_LEDS 0
// SPI spi(SPI_MOSI, SPI_MISO, SPI_SCK);
// Serial serial(SERIAL_TX, SERIAL_RX);
#define analogIn4_IS_HIGH_RANGE_OF_analogIn0 0
const float ADC_FULL_SCALE_VOLTAGE = 3.300;     // TODO: ADC_FULL_SCALE_VOLTAGE Pico?
//
//--------------------------------------------------
#elif defined(TARGET_LPC1768)
//--------------------------------------------------
// TARGET=LPC1768 ARM Cortex-M3 100 MHz 512kB flash 64kB SRAM
//               +-------------[microUSB]-------------+
//        ______ | [ ] GND             +3.3V VOUT [ ] | ______
//        ______ | [ ] 4.5V<VIN<9.0V   +5.0V VU   [ ] | ______
//        ______ | [ ] VB                 USB.IF- [ ] | ______
//        ______ | [ ] nR                 USB.IF+ [ ] | ______
// digitalInOut0 | [ ] p5 MOSI       ETHERNET.RD- [ ] | ______
// digitalInOut1 | [ ] p6 MISO       ETHERNET.RD+ [ ] | ______
// digitalInOut2 | [ ] p7 SCLK       ETHERNET.TD- [ ] | ______
// digitalInOut3 | [ ] p8            ETHERNET.TD+ [ ] | ______
// digitalInOut4 | [ ] p9  TX SDA          USB.D- [ ] | ______
// digitalInOut5 | [ ] p10 RX SCL          USB.D+ [ ] | ______
// digitalInOut6 | [ ] p11    MOSI     CAN-RD p30 [ ] | digitalInOut13
// digitalInOut7 | [ ] p12    MISO     CAN-TD p29 [ ] | digitalInOut12
// digitalInOut8 | [ ] p13 TX SCLK     SDA TX p28 [ ] | digitalInOut11
// digitalInOut9 | [ ] p14 RX          SCL RX p27 [ ] | digitalInOut10
//     analogIn0 | [ ] p15 AIN0 3.3Vfs   PWM1 p26 [ ] | pwmDriver1
//     analogIn1 | [ ] p16 AIN1 3.3Vfs   PWM2 p25 [ ] | pwmDriver2
//     analogIn2 | [ ] p17 AIN2 3.3Vfs   PWM3 p24 [ ] | pwmDriver3
//     analogIn3 | [ ] p18 AIN3 AOUT     PWM4 p23 [ ] | pwmDriver4
//     analogIn4 | [ ] p19 AIN4 3.3Vfs   PWM5 p22 [ ] | pwmDriver5
//     analogIn5 | [ ] p20 AIN5 3.3Vfs   PWM6 p21 [ ] | pwmDriver6
//               +------------------------------------+
// AIN6 = P0.3 = TGT_SBL_RXD?
// AIN7 = P0.2 = TGT_SBL_TXD?
//
//--------------------------------------------------
// LPC1768 board uses VREF = 3.300V +A3,3V thru L1 to bypass capacitor C14
#define analogIn4_IS_HIGH_RANGE_OF_analogIn0 0
const float ADC_FULL_SCALE_VOLTAGE = 3.300;
#else // not defined(TARGET_LPC1768 etc.)
//--------------------------------------------------
// unknown target
//--------------------------------------------------
#endif // target definition




// support LEDs as digital pins 91 92 93; WIP button as digital pin 90
// move definiton of USE_LEDS earlier than find_digitalInOutPin()
//--------------------------------------------------
// Option to use LEDs to show status
#ifndef USE_LEDS
#define USE_LEDS 1
#endif
#if USE_LEDS
#if defined(TARGET_MAX32630)
# define LED_ON  0
# define LED_OFF 1
//--------------------------------------------------
#elif defined(TARGET_MAX32625MBED)
# define LED_ON  0
# define LED_OFF 1
#elif defined(TARGET_MAX32625PICO) || defined(TARGET_MAX40108DEMOP2U9)
# define LED_ON  0
# define LED_OFF 1
//--------------------------------------------------
// TODO1: TARGET=MAX32625MBED ARM Cortex-M4F 96MHz 512kB Flash 160kB SRAM
#elif defined(TARGET_LPC1768)
# define LED_ON  1
# define LED_OFF 0
#else // not defined(TARGET_LPC1768 etc.)
// USE_LEDS with some platform other than MAX32630, MAX32625MBED, LPC1768
// bugfix for MAX32600MBED LED blink pattern: check if LED_ON/LED_OFF already defined
# ifndef LED_ON
#  define LED_ON  0
# endif
# ifndef LED_OFF
#  define LED_OFF 1
# endif
//# define LED_ON  1
//# define LED_OFF 0
#endif // target definition
// support LEDs as digital pins 91 92 93; WIP button as digital pin 90
// support find_digitalInOutPin(91) return DigitalInOut of led1_RFailLED
// support find_digitalInOutPin(92) return DigitalInOut of led2_GPassLED
// support find_digitalInOutPin(93) return DigitalInOut of led3_BBusyLED
// change led1/2/3/4 from DigitalOut to DigitalInOut
DigitalInOut led1(LED1, PIN_INPUT, PullUp, LED_OFF); // MAX32630FTHR: LED1 = LED_RED
DigitalInOut led2(LED2, PIN_INPUT, PullUp, LED_OFF); // MAX32630FTHR: LED2 = LED_GREEN
DigitalInOut led3(LED3, PIN_INPUT, PullUp, LED_OFF); // MAX32630FTHR: LED3 = LED_BLUE
DigitalInOut led4(LED4, PIN_INPUT, PullUp, LED_OFF);
#else // USE_LEDS=0
// issue #41 support Nucleo_F446RE
// there are no LED indicators on the board, LED1 interferes with SPI;
// but we still need placeholders led1 led2 led3 led4.
// Declare DigitalInOut led1 led2 led3 led4 targeting safe pins.
// PinName NC means NOT_CONNECTED; DigitalOut::is_connected() returns false
# define LED_ON  0
# define LED_OFF 1
// change led1/2/3/4 from DigitalOut to DigitalInOut
DigitalInOut led1(NC, PIN_INPUT, PullUp, LED_OFF);
DigitalInOut led2(NC, PIN_INPUT, PullUp, LED_OFF);
DigitalInOut led3(NC, PIN_INPUT, PullUp, LED_OFF);
DigitalInOut led4(NC, PIN_INPUT, PullUp, LED_OFF);
#endif // USE_LEDS
#define led1_RFailLED led1
#define led2_GPassLED led2
#define led3_BBusyLED led3

//--------------------------------------------------
// use BUTTON1 trigger some action
#if defined(TARGET_MAX32630)
#define HAS_BUTTON1_DEMO_INTERRUPT 1
#define HAS_BUTTON2_DEMO 0
#define HAS_BUTTON2_DEMO_INTERRUPT 0
#elif defined(TARGET_MAX32625PICO) || defined(TARGET_MAX40108DEMOP2U9)
// #warning "TARGET_MAX32625PICO not previously tested; need to define buttons..."
#define HAS_BUTTON1_DEMO_INTERRUPT 1
#if MAX40108_DEMO
#if HAS_I2C
// MAX40108DEMOP2U9 HAS_I2C: J91.1=1V8 J91.2=P1_6(SDA) J91.3=P1_7(SCL) J91.4=GND
// MAX40108DEMOP2U9 HAS_I2C: move button2/button3 to inaccessible pins P3_6 and P3_7 if we need J91 for I2C
#define BUTTON2 P3_7
#else // HAS_I2C
// MAX40108DEMOP2U9 no HAS_I2C: option using J91 for button2 and button3 instead of I2C
// MAX40108DEMOP2U9 no HAS_I2C: header J91.1=1V8 J91.2=P1_6(button3/'%B3!') J91.3=P1_7(button2/'%B2!') J91.4=GND
// MAX40108DEMOP2U9 no HAS_I2C: avoid conflict between digital pins D16 D17 and button2/button3
#define BUTTON2 P1_7
#endif // HAS_I2C
#define HAS_BUTTON2_DEMO 0
#define HAS_BUTTON2_DEMO_INTERRUPT 1
#if HAS_I2C
// MAX40108DEMOP2U9 HAS_I2C: J91.1=1V8 J91.2=P1_6(SDA) J91.3=P1_7(SCL) J91.4=GND
// MAX40108DEMOP2U9 HAS_I2C: move button2/button3 to inaccessible pins P3_6 and P3_7 if we need J91 for I2C
#define BUTTON3 P3_6
#else // HAS_I2C
// MAX40108DEMOP2U9 no HAS_I2C: option using J91 for button2 and button3 instead of I2C
// MAX40108DEMOP2U9 no HAS_I2C: header J91.1=1V8 J91.2=P1_6(button3/'%B3!') J91.3=P1_7(button2/'%B2!') J91.4=GND
// MAX40108DEMOP2U9 no HAS_I2C: avoid conflict between digital pins D16 D17 and button2/button3
#define BUTTON3 P1_6
#endif // HAS_I2C
#define HAS_BUTTON3_DEMO 0
#define HAS_BUTTON3_DEMO_INTERRUPT 1
// additional buttons are assigned to unused/unaccessible pins to avoid conflicts
// activate using %B4! or action_button pin=4
#define BUTTON4 P1_5
#define HAS_BUTTON4_DEMO_INTERRUPT 1
#define BUTTON5 P1_4
#define HAS_BUTTON5_DEMO_INTERRUPT 1
#define BUTTON6 P1_3
#define HAS_BUTTON6_DEMO_INTERRUPT 1
#define BUTTON7 P1_2
#define HAS_BUTTON7_DEMO_INTERRUPT 1
#define BUTTON8 P1_1
#define HAS_BUTTON8_DEMO_INTERRUPT 1
#define BUTTON9 P1_0
#define HAS_BUTTON9_DEMO_INTERRUPT 1
#else // MAX40108_DEMO
#define HAS_BUTTON2_DEMO 0
#define HAS_BUTTON2_DEMO_INTERRUPT 0
#endif // MAX40108_DEMO ---------------------------------
#elif defined(TARGET_MAX32625)
#define HAS_BUTTON1_DEMO_INTERRUPT 1
#define HAS_BUTTON2_DEMO_INTERRUPT 1
#elif defined(TARGET_MAX32620FTHR)
#warning "TARGET_MAX32620FTHR not previously tested; need to define buttons..."
#define BUTTON1 SW1
#define HAS_BUTTON1_DEMO_INTERRUPT 1
#define HAS_BUTTON2_DEMO 0
#define HAS_BUTTON2_DEMO_INTERRUPT 0
#elif defined(TARGET_NUCLEO_F446RE)
#define HAS_BUTTON1_DEMO_INTERRUPT 0
#define HAS_BUTTON2_DEMO_INTERRUPT 0
#elif defined(TARGET_NUCLEO_F401RE)
#define HAS_BUTTON1_DEMO_INTERRUPT 0
#define HAS_BUTTON2_DEMO_INTERRUPT 0
#else
#warning "target not previously tested; need to define buttons..."
#endif
//
#ifndef HAS_BUTTON1_DEMO
#define HAS_BUTTON1_DEMO 0
#endif
#ifndef HAS_BUTTON2_DEMO
#define HAS_BUTTON2_DEMO 0
#endif
#ifndef HAS_BUTTON3_DEMO
#define HAS_BUTTON3_DEMO 0
#endif
//
// avoid runtime error on button1 press [mbed-os-5.11]
// instead of using InterruptIn, use DigitalIn and poll in main while(1)
#ifndef HAS_BUTTON1_DEMO_INTERRUPT_POLLING
#define HAS_BUTTON1_DEMO_INTERRUPT_POLLING 1
#endif
//
#ifndef HAS_BUTTON1_DEMO_INTERRUPT
#define HAS_BUTTON1_DEMO_INTERRUPT 1
#endif
#ifndef HAS_BUTTON2_DEMO_INTERRUPT
#define HAS_BUTTON2_DEMO_INTERRUPT 1
#endif
//
#if HAS_BUTTON1_DEMO_INTERRUPT
# if HAS_BUTTON1_DEMO_INTERRUPT_POLLING
// avoid runtime error on button1 press [mbed-os-5.11]
// instead of using InterruptIn, use DigitalIn and poll in main while(1)
DigitalIn button1(BUTTON1);
# else
InterruptIn button1(BUTTON1);
# endif
#elif HAS_BUTTON1_DEMO
DigitalIn button1(BUTTON1);
#endif
#if HAS_BUTTON2_DEMO_INTERRUPT
# if HAS_BUTTON1_DEMO_INTERRUPT_POLLING
// avoid runtime error on button1 press [mbed-os-5.11]
// instead of using InterruptIn, use DigitalIn and poll in main while(1)
DigitalIn button2(BUTTON2);
# else
InterruptIn button2(BUTTON2);
# endif
#elif HAS_BUTTON2_DEMO
DigitalIn button2(BUTTON2);
#endif
#if HAS_BUTTON3_DEMO_INTERRUPT
# if HAS_BUTTON1_DEMO_INTERRUPT_POLLING
// avoid runtime error on button1 press [mbed-os-5.11]
// instead of using InterruptIn, use DigitalIn and poll in main while(1)
DigitalIn button3(BUTTON3);
# else
InterruptIn button3(BUTTON3);
# endif
#elif HAS_BUTTON3_DEMO
DigitalIn button3(BUTTON3);
#endif

// uncrustify-0.66.1 *INDENT-OFF*
//--------------------------------------------------
// Declare the DigitalInOut GPIO pins
// Optional digitalInOut support. If there is only one it should be digitalInOut1.
// D) Digital High/Low/Input Pin
#if defined(TARGET_MAX32630)
//       +-------------[microUSB]-------------+
//       | J1         MAX32630FTHR        J2  |
//       | [ ] RST                    GND [ ] |
//       | [ ] 3V3                    BAT+[ ] |
//       | [ ] 1V8                  reset SW1 |
//       | [ ] GND       J4               J3  |
//       | [ ] AIN_0 1.2Vfs     (bat) SYS [ ] |
//       | [ ] AIN_1 1.2Vfs           PWR [ ] |
//       | [ ] AIN_2 1.2Vfs      +5V VBUS [ ] |
//       | [ ] AIN_3 1.2Vfs   1-WIRE P4_0 [ ] | dig9
// dig10 | [x] P5_7  SDA2        SRN P5_6 [ ] | dig8
// dig11 | [x] P6_0  SCL2      SDIO3 P5_5 [ ] | dig7
// dig12 | [x] P5_0  SCLK      SDIO2 P5_4 [ ] | dig6
// dig13 | [x] P5_1  MOSI       SSEL P5_3 [x] | dig5
// dig14 | [ ] P5_2  MISO        RTS P3_3 [ ] | dig4
// dig15 | [ ] P3_0  RX          CTS P3_2 [ ] | dig3
// dig0  | [ ] P3_1  TX          SCL P3_5 [x] | dig2
//       | [ ] GND               SDA P3_4 [x] | dig1
//       +------------------------------------+
    #define HAS_digitalInOut0 1 // P3_1 TARGET_MAX32630 J1.15
    #define HAS_digitalInOut1 1 // P3_4 TARGET_MAX32630 J3.12
    #define HAS_digitalInOut2 1 // P3_5 TARGET_MAX32630 J3.11
    #define HAS_digitalInOut3 1 // P3_2 TARGET_MAX32630 J3.10
    #define HAS_digitalInOut4 1 // P3_3 TARGET_MAX32630 J3.9
    #define HAS_digitalInOut5 1 // P5_3 TARGET_MAX32630 J3.8
    #define HAS_digitalInOut6 1 // P5_4 TARGET_MAX32630 J3.7
    #define HAS_digitalInOut7 1 // P5_5 TARGET_MAX32630 J3.6
    #define HAS_digitalInOut8 1 // P5_6 TARGET_MAX32630 J3.5
    #define HAS_digitalInOut9 1 // P4_0 TARGET_MAX32630 J3.4
#if HAS_I2C
// avoid resource conflict between P5_7, P6_0 I2C and DigitalInOut
    #define HAS_digitalInOut10 0 // P5_7 TARGET_MAX32630 J1.9
    #define HAS_digitalInOut11 0 // P6_0 TARGET_MAX32630 J1.10
#else // HAS_I2C
    #define HAS_digitalInOut10 1 // P5_7 TARGET_MAX32630 J1.9
    #define HAS_digitalInOut11 1 // P6_0 TARGET_MAX32630 J1.10
#endif // HAS_I2C
#if HAS_SPI
// avoid resource conflict between P5_0, P5_1, P5_2 SPI and DigitalInOut
    #define HAS_digitalInOut12 0 // P5_0 TARGET_MAX32630 J1.11
    #define HAS_digitalInOut13 0 // P5_1 TARGET_MAX32630 J1.12
    #define HAS_digitalInOut14 0 // P5_2 TARGET_MAX32630 J1.13
    #define HAS_digitalInOut15 0 // P3_0 TARGET_MAX32630 J1.14
#else // HAS_SPI
    #define HAS_digitalInOut12 1 // P5_0 TARGET_MAX32630 J1.11
    #define HAS_digitalInOut13 1 // P5_1 TARGET_MAX32630 J1.12
    #define HAS_digitalInOut14 1 // P5_2 TARGET_MAX32630 J1.13
    #define HAS_digitalInOut15 1 // P3_0 TARGET_MAX32630 J1.14
#endif // HAS_SPI
#if HAS_digitalInOut0
    DigitalInOut digitalInOut0(P3_1, PIN_INPUT, PullUp, 1); // P3_1 TARGET_MAX32630 J1.15
#endif
#if HAS_digitalInOut1
    DigitalInOut digitalInOut1(P3_4, PIN_INPUT, PullUp, 1); // P3_4 TARGET_MAX32630 J3.12
#endif
#if HAS_digitalInOut2
    DigitalInOut digitalInOut2(P3_5, PIN_INPUT, PullUp, 1); // P3_5 TARGET_MAX32630 J3.11
#endif
#if HAS_digitalInOut3
    DigitalInOut digitalInOut3(P3_2, PIN_INPUT, PullUp, 1); // P3_2 TARGET_MAX32630 J3.10
#endif
#if HAS_digitalInOut4
    DigitalInOut digitalInOut4(P3_3, PIN_INPUT, PullUp, 1); // P3_3 TARGET_MAX32630 J3.9
#endif
#if HAS_digitalInOut5
    DigitalInOut digitalInOut5(P5_3, PIN_INPUT, PullUp, 1); // P5_3 TARGET_MAX32630 J3.8
#endif
#if HAS_digitalInOut6
    DigitalInOut digitalInOut6(P5_4, PIN_INPUT, PullUp, 1); // P5_4 TARGET_MAX32630 J3.7
#endif
#if HAS_digitalInOut7
    DigitalInOut digitalInOut7(P5_5, PIN_INPUT, PullUp, 1); // P5_5 TARGET_MAX32630 J3.6
#endif
#if HAS_digitalInOut8
    DigitalInOut digitalInOut8(P5_6, PIN_INPUT, PullUp, 1); // P5_6 TARGET_MAX32630 J3.5
#endif
#if HAS_digitalInOut9
    DigitalInOut digitalInOut9(P4_0, PIN_INPUT, PullUp, 1); // P4_0 TARGET_MAX32630 J3.4
#endif
#if HAS_digitalInOut10
    DigitalInOut digitalInOut10(P5_7, PIN_INPUT, PullUp, 1); // P5_7 TARGET_MAX32630 J1.9
#endif
#if HAS_digitalInOut11
    DigitalInOut digitalInOut11(P6_0, PIN_INPUT, PullUp, 1); // P6_0 TARGET_MAX32630 J1.10
#endif
#if HAS_digitalInOut12
    DigitalInOut digitalInOut12(P5_0, PIN_INPUT, PullUp, 1); // P5_0 TARGET_MAX32630 J1.11
#endif
#if HAS_digitalInOut13
    DigitalInOut digitalInOut13(P5_1, PIN_INPUT, PullUp, 1); // P5_1 TARGET_MAX32630 J1.12
#endif
#if HAS_digitalInOut14
    DigitalInOut digitalInOut14(P5_2, PIN_INPUT, PullUp, 1); // P5_2 TARGET_MAX32630 J1.13
#endif
#if HAS_digitalInOut15
    DigitalInOut digitalInOut15(P3_0, PIN_INPUT, PullUp, 1); // P3_0 TARGET_MAX32630 J1.14
#endif
//--------------------------------------------------
#elif defined(TARGET_MAX32625MBED)
// TARGET=MAX32625MBED ARM Cortex-M4F 96MHz 512kB Flash 160kB SRAM
//             +-------------------------------------+
//             |   MAX32625MBED Arduino UNO header   |
//             |                                     |
//             |                           A5/SCL[ ] |   P1_7 dig15
//             |                           A4/SDA[ ] |   P1_6 dig14
//             |                         AREF=N/C[ ] |
//             |                              GND[ ] |
//             | [ ]N/C                    SCK/13[ ] |   P1_0 dig13
//             | [ ]IOREF=3V3             MISO/12[ ] |   P1_2 dig12
//             | [ ]RST                   MOSI/11[ ]~|   P1_1 dig11
//             | [ ]3V3                     CS/10[ ]~|   P1_3 dig10
//             | [ ]5V0                         9[ ]~|   P1_5 dig9
//             | [ ]GND                         8[ ] |   P1_4 dig8
//             | [ ]GND                              |
//             | [ ]Vin                         7[ ] |   P0_7 dig7
//             |                                6[ ]~|   P0_6 dig6
//       AIN_0 | [ ]A0                          5[ ]~|   P0_5 dig5
//       AIN_1 | [ ]A1                          4[ ] |   P0_4 dig4
//       AIN_2 | [ ]A2                     INT1/3[ ]~|   P0_3 dig3
//       AIN_3 | [ ]A3                     INT0/2[ ] |   P0_2 dig2
// dig16  P3_4 | [ ]A4/SDA  RST SCK MISO     TX>1[ ] |   P0_1 dig1
// dig17  P3_5 | [ ]A5/SCL  [ ] [ ] [ ]      RX<0[ ] |   P0_0 dig0
//             |            [ ] [ ] [ ]              |
//             |  UNO_R3    GND MOSI 5V  ____________/
//              \_______________________/
//
    #define HAS_digitalInOut0 1 // P0_0 TARGET_MAX32625MBED D0
    #define HAS_digitalInOut1 1 // P0_1 TARGET_MAX32625MBED D1
#if APPLICATION_MAX11131
    #define HAS_digitalInOut2 0 // P0_2 TARGET_MAX32625MBED D2 -- MAX11131 EOC DigitalIn
#else
    #define HAS_digitalInOut2 1 // P0_2 TARGET_MAX32625MBED D2
#endif
    #define HAS_digitalInOut3 1 // P0_3 TARGET_MAX32625MBED D3
    #define HAS_digitalInOut4 1 // P0_4 TARGET_MAX32625MBED D4
    #define HAS_digitalInOut5 1 // P0_5 TARGET_MAX32625MBED D5
    #define HAS_digitalInOut6 1 // P0_6 TARGET_MAX32625MBED D6
    #define HAS_digitalInOut7 1 // P0_7 TARGET_MAX32625MBED D7
    #define HAS_digitalInOut8 1 // P1_4 TARGET_MAX32625MBED D8
#if APPLICATION_MAX11131
    #define HAS_digitalInOut9 0 // P1_5 TARGET_MAX32625MBED D9 -- MAX11131 CNVST DigitalOut
#else
    #define HAS_digitalInOut9 1 // P1_5 TARGET_MAX32625MBED D9
#endif
#if HAS_SPI
// avoid resource conflict between P5_0, P5_1, P5_2 SPI and DigitalInOut
    #define HAS_digitalInOut10 0 // P1_3 TARGET_MAX32635MBED CS/10
    #define HAS_digitalInOut11 0 // P1_1 TARGET_MAX32635MBED MOSI/11
    #define HAS_digitalInOut12 0 // P1_2 TARGET_MAX32635MBED MISO/12
    #define HAS_digitalInOut13 0 // P1_0 TARGET_MAX32635MBED SCK/13
#else // HAS_SPI
    #define HAS_digitalInOut10 1 // P1_3 TARGET_MAX32635MBED CS/10
    #define HAS_digitalInOut11 1 // P1_1 TARGET_MAX32635MBED MOSI/11
    #define HAS_digitalInOut12 1 // P1_2 TARGET_MAX32635MBED MISO/12
    #define HAS_digitalInOut13 1 // P1_0 TARGET_MAX32635MBED SCK/13
#endif // HAS_SPI
#if HAS_I2C
// avoid resource conflict between P5_7, P6_0 I2C and DigitalInOut
    #define HAS_digitalInOut14 0 // P1_6 TARGET_MAX32635MBED A4/SDA (10pin digital connector)
    #define HAS_digitalInOut15 0 // P1_7 TARGET_MAX32635MBED A5/SCL (10pin digital connector)
    #define HAS_digitalInOut16 0 // P3_4 TARGET_MAX32635MBED A4/SDA (6pin analog connector)
    #define HAS_digitalInOut17 0 // P3_5 TARGET_MAX32635MBED A5/SCL (6pin analog connector)
#else // HAS_I2C
    #define HAS_digitalInOut14 1 // P1_6 TARGET_MAX32635MBED A4/SDA (10pin digital connector)
    #define HAS_digitalInOut15 1 // P1_7 TARGET_MAX32635MBED A5/SCL (10pin digital connector)
    #define HAS_digitalInOut16 1 // P3_4 TARGET_MAX32635MBED A4/SDA (6pin analog connector)
    #define HAS_digitalInOut17 1 // P3_5 TARGET_MAX32635MBED A5/SCL (6pin analog connector)
#endif // HAS_I2C
#if HAS_digitalInOut0
    DigitalInOut digitalInOut0(P0_0, PIN_INPUT, PullUp, 1); // P0_0 TARGET_MAX32625MBED D0
#endif
#if HAS_digitalInOut1
    DigitalInOut digitalInOut1(P0_1, PIN_INPUT, PullUp, 1); // P0_1 TARGET_MAX32625MBED D1
#endif
#if HAS_digitalInOut2
    DigitalInOut digitalInOut2(P0_2, PIN_INPUT, PullUp, 1); // P0_2 TARGET_MAX32625MBED D2
#endif
#if HAS_digitalInOut3
    DigitalInOut digitalInOut3(P0_3, PIN_INPUT, PullUp, 1); // P0_3 TARGET_MAX32625MBED D3
#endif
#if HAS_digitalInOut4
    DigitalInOut digitalInOut4(P0_4, PIN_INPUT, PullUp, 1); // P0_4 TARGET_MAX32625MBED D4
#endif
#if HAS_digitalInOut5
    DigitalInOut digitalInOut5(P0_5, PIN_INPUT, PullUp, 1); // P0_5 TARGET_MAX32625MBED D5
#endif
#if HAS_digitalInOut6
    DigitalInOut digitalInOut6(P0_6, PIN_INPUT, PullUp, 1); // P0_6 TARGET_MAX32625MBED D6
#endif
#if HAS_digitalInOut7
    DigitalInOut digitalInOut7(P0_7, PIN_INPUT, PullUp, 1); // P0_7 TARGET_MAX32625MBED D7
#endif
#if HAS_digitalInOut8
    DigitalInOut digitalInOut8(P1_4, PIN_INPUT, PullUp, 1); // P1_4 TARGET_MAX32625MBED D8
#endif
#if HAS_digitalInOut9
    DigitalInOut digitalInOut9(P1_5, PIN_INPUT, PullUp, 1); // P1_5 TARGET_MAX32625MBED D9
#endif
#if HAS_digitalInOut10
    DigitalInOut digitalInOut10(P1_3, PIN_INPUT, PullUp, 1); // P1_3 TARGET_MAX32635MBED CS/10
#endif
#if HAS_digitalInOut11
    DigitalInOut digitalInOut11(P1_1, PIN_INPUT, PullUp, 1); // P1_1 TARGET_MAX32635MBED MOSI/11
#endif
#if HAS_digitalInOut12
    DigitalInOut digitalInOut12(P1_2, PIN_INPUT, PullUp, 1); // P1_2 TARGET_MAX32635MBED MISO/12
#endif
#if HAS_digitalInOut13
    DigitalInOut digitalInOut13(P1_0, PIN_INPUT, PullUp, 1); // P1_0 TARGET_MAX32635MBED SCK/13
#endif
#if HAS_digitalInOut14
    // Ensure that the unused I2C pins do not interfere with analog inputs A4 and A5
    // DigitalInOut mode can be one of PullUp, PullDown, PullNone, OpenDrain
    DigitalInOut digitalInOut14(P1_6, PIN_INPUT, OpenDrain, 1); // P1_6 TARGET_MAX32635MBED A4/SDA (10pin digital connector)
#endif
#if HAS_digitalInOut15
    // Ensure that the unused I2C pins do not interfere with analog inputs A4 and A5
    DigitalInOut digitalInOut15(P1_7, PIN_INPUT, OpenDrain, 1); // P1_7 TARGET_MAX32635MBED A5/SCL (10pin digital connector)
#endif
#if HAS_digitalInOut16
    // Ensure that the unused I2C pins do not interfere with analog inputs A4 and A5
    // DigitalInOut mode can be one of PullUp, PullDown, PullNone, OpenDrain
    // PullUp-->3.4V, PullDown-->1.7V, PullNone-->3.5V, OpenDrain-->0.00V
    DigitalInOut digitalInOut16(P3_4, PIN_INPUT, OpenDrain, 0); // P3_4 TARGET_MAX32635MBED A4/SDA (6pin analog connector)
#endif
#if HAS_digitalInOut17
    // Ensure that the unused I2C pins do not interfere with analog inputs A4 and A5
    DigitalInOut digitalInOut17(P3_5, PIN_INPUT, OpenDrain, 0); // P3_5 TARGET_MAX32635MBED A5/SCL (6pin analog connector)
#endif
//--------------------------------------------------
#elif defined(TARGET_MAX32625PICO) || defined(TARGET_MAX40108DEMOP2U9)
// TARGET=MAX32625PICO ARM Cortex-M4F 96MHz 512kB Flash 160kB SRAM
//             +-------------[microUSB]-------------+
//             |           [27]D+ D-[26]            |
//             |                                    |
//             |           [BUTTON P2_7]            |
//             |  P2_4 LED_R P2_5 LED_G P2_6 LED_B  |
//             |                                    |
//         1V8 | [11] 1.8V  MAX32625PICO   GND [10] | GND
//         3V3 | [12] 3.3V                 +5V [09] | 5V0
// SPI CS   D7 | [13] P0_7 CS      s-ssel P4_7 [08] | D15
// SPI MISO D6 | [14] P0_6 MISO    s-miso P4_6 [07] | D14
// SPI MOSI D5 | [15] P0_5 MOSI    s-mosi P4_5 [06] | D13
// SPI SCLK D4 | [16] P0_4 SCLK    s-sclk P4_4 [05] | D12
//          D3 | [17] P0_3 RTS        SCL P1_7 [04] | SCL/D17
//          D2 | [18] P0_2 CTS        SDA P1_6 [03] | SDA/D16
//       TX/D1 | [19] P0_1 TX0           AIN_2 [02] | A2
//       RX/D0 | [20] P0_0 RX0           AIN_0 [01] | A0/A4
//             |                                    |
//             |                  DAPLINK           |
//             |  J3   p3_3 p3_2 p3_0 p3_1 p3_7     |
//             |  DAP  [  ] [  ] [RX2][TX2][  ]     |
//             |  TOP  [  ] [  ] [  ] [  ] [  ]     |
//             |    AIN1/A5 gnd  gnd   nc  AIN3     |
//             |       IOH                 1-wire   |
//             |                                    |
//             |NO USE RST       P2_0      P2_1     |
//             |BOTTOM [  ] [  ] [RX1][  ] [TX1]    |
//             |       RST  SWC  GND  SWD  1V8      |
//             |BOTTOM [21] [22] [23] [24] [25]     |
//             +------------------------------------+
#if MAX40108_DEMO
// MAX40108 demo p2: D0..D7 = P0_0..P0_7; D8..15 = P4_0..P4_7; D16/D17=I2C
#endif
// AIN_0 = AIN0 pin       fullscale is 1.2V
// AIN_1 = AIN1 pin       fullscale is 1.2V
// AIN_2 = AIN2 pin       fullscale is 1.2V
// AIN_3 = AIN3 pin       fullscale is 1.2V
// AIN_4 = AIN0 / 5.0     fullscale is 6.0V
// AIN_5 = AIN1 / 5.0     fullscale is 6.0V
// AIN_6 = VDDB / 4.0     fullscale is 4.8V
// AIN_7 = VDD18          fullscale is 1.2V
// AIN_8 = VDD12          fullscale is 1.2V
// AIN_9 = VRTC / 2.0     fullscale is 2.4V
// AIN_10 = x undefined?
// AIN_11 = VDDIO / 4.0   fullscale is 4.8V
// AIN_12 = VDDIOH / 4.0  fullscale is 4.8V
//
#if MAX40108_DEMO
// avoid resource conflict D0,D1 alternate function as RX/TX
    #define HAS_digitalInOut0 0
    #define HAS_digitalInOut1 0
#else
    #define HAS_digitalInOut0 1
    #define HAS_digitalInOut1 1
    // P0_1 TARGET_MAX32625PICO D1
#endif
#if APPLICATION_MAX11131
// avoid resource conflict D2 alternate function as interrupt input
    #define HAS_digitalInOut2 0
#else
    #define HAS_digitalInOut2 1
#endif
    #define HAS_digitalInOut3 1
    #define HAS_digitalInOut4 1
    #define HAS_digitalInOut5 1
    #define HAS_digitalInOut6 1
    #define HAS_digitalInOut7 1
    //
    #define HAS_digitalInOut8 1
    #define HAS_digitalInOut9 1
    #define HAS_digitalInOut10 1
    #define HAS_digitalInOut11 1
    #define HAS_digitalInOut12 1
    #define HAS_digitalInOut13 1
    #define HAS_digitalInOut14 1
    #define HAS_digitalInOut15 1
#if HAS_I2C
// MAX40108DEMOP2U9 HAS_I2C: J91.1=1V8 J91.2=P1_6(SDA) J91.3=P1_7(SCL) J91.4=GND
// MAX40108DEMOP2U9 HAS_I2C: move button2/button3 to inaccessible pins P3_6 and P3_7 if we need J91 for I2C
// #define BUTTON2 P3_7
// avoid resource conflict between I2C and DigitalInOut
    #define HAS_digitalInOut16 0 // P1_6 TARGET_MAX40108DEMOP2U9 J91.2=P1_6(button3/'%B3!'/SDA)
    #define HAS_digitalInOut17 0 // P1_7 TARGET_MAX40108DEMOP2U9 J91.3=P1_7(button2/'%B2!'/SCL)
#else // HAS_I2C
// MAX40108DEMOP2U9 no HAS_I2C: option using J91 for button2 and button3 instead of I2C
// MAX40108DEMOP2U9 no HAS_I2C: header J91.1=1V8 J91.2=P1_6(button3/'%B3!') J91.3=P1_7(button2/'%B2!') J91.4=GND
// MAX40108DEMOP2U9 no HAS_I2C: avoid conflict between digital pins D16 D17 and button2/button3
// #define BUTTON2 P1_7
#if HAS_BUTTON3_DEMO_INTERRUPT
// MAX40108DEMOP2U9 avoid conflict between digital pins D16 D17 and button2/button3
    #define HAS_digitalInOut16 0 // P1_6 TARGET_MAX40108DEMOP2U9 J91.2=P1_6(button3/'%B3!'/SDA)
#else // HAS_BUTTON3_DEMO_INTERRUPT
    #define HAS_digitalInOut16 1 // P1_6 TARGET_MAX40108DEMOP2U9 J91.2=P1_6(button3/'%B3!'/SDA)
#endif // HAS_BUTTON3_DEMO_INTERRUPT
#if HAS_BUTTON2_DEMO_INTERRUPT
// MAX40108DEMOP2U9 avoid conflict between digital pins D16 D17 and button2/button3
    #define HAS_digitalInOut17 0 // P1_7 TARGET_MAX40108DEMOP2U9 J91.3=P1_7(button2/'%B2!'/SCL)
#else // HAS_BUTTON2_DEMO_INTERRUPT
    #define HAS_digitalInOut17 1 // P1_7 TARGET_MAX40108DEMOP2U9 J91.3=P1_7(button2/'%B2!'/SCL)
#endif // HAS_BUTTON2_DEMO_INTERRUPT
#endif // HAS_I2C
#if HAS_digitalInOut0
    DigitalInOut digitalInOut0(P0_0, PIN_INPUT, PullUp, 1); // P0_0 TARGET_MAX32625PICO TARGET_MAX40108DEMOP2U9 D0
#endif
#if HAS_digitalInOut1
    DigitalInOut digitalInOut1(P0_1, PIN_INPUT, PullUp, 1); // P0_1 TARGET_MAX32625PICO TARGET_MAX40108DEMOP2U9 D1
#endif
#if HAS_digitalInOut2
    DigitalInOut digitalInOut2(P0_2, PIN_INPUT, PullUp, 1); // P0_2 TARGET_MAX32625PICO TARGET_MAX40108DEMOP2U9 D2
#endif
#if HAS_digitalInOut3
    DigitalInOut digitalInOut3(P0_3, PIN_INPUT, PullUp, 1); // P0_3 TARGET_MAX32625PICO TARGET_MAX40108DEMOP2U9 D3
#endif
#if HAS_digitalInOut4
    DigitalInOut digitalInOut4(P0_4, PIN_INPUT, PullUp, 1); // P0_4 TARGET_MAX32625PICO TARGET_MAX40108DEMOP2U9 D4
#endif
#if HAS_digitalInOut5
    DigitalInOut digitalInOut5(P0_5, PIN_INPUT, PullUp, 1); // P0_5 TARGET_MAX32625PICO TARGET_MAX40108DEMOP2U9 D5
#endif
#if HAS_digitalInOut6
    DigitalInOut digitalInOut6(P0_6, PIN_INPUT, PullUp, 1); // P0_6 TARGET_MAX32625PICO TARGET_MAX40108DEMOP2U9 D6
#endif
#if HAS_digitalInOut7
    DigitalInOut digitalInOut7(P0_7, PIN_INPUT, PullUp, 1); // P0_7 TARGET_MAX32625PICO TARGET_MAX40108DEMOP2U9 D7
#endif
#if HAS_digitalInOut8
    DigitalInOut digitalInOut8(P4_0, PIN_INPUT, PullUp, 1); // P4_0 TARGET_MAX40108DEMOP2U9 D8
#endif
#if HAS_digitalInOut9
    DigitalInOut digitalInOut9(P4_1, PIN_INPUT, PullUp, 1); // P4_1 TARGET_MAX40108DEMOP2U9 D9
#endif
#if HAS_digitalInOut10
    DigitalInOut digitalInOut10(P4_2, PIN_INPUT, PullUp, 1); // P4_2 TARGET_MAX40108DEMOP2U9 D10
#endif
#if HAS_digitalInOut11
    DigitalInOut digitalInOut11(P4_3, PIN_INPUT, PullUp, 1); // P4_3 TARGET_MAX40108DEMOP2U9 D11
#endif
#if HAS_digitalInOut12
    DigitalInOut digitalInOut12(P4_4, PIN_INPUT, PullUp, 1); // P4_4 TARGET_MAX32625PICO TARGET_MAX40108DEMOP2U9 D12
#endif
#if HAS_digitalInOut13
    DigitalInOut digitalInOut13(P4_5, PIN_INPUT, PullUp, 1); // P4_5 TARGET_MAX32625PICO TARGET_MAX40108DEMOP2U9 D13
#endif
#if HAS_digitalInOut14
    DigitalInOut digitalInOut14(P4_6, PIN_INPUT, PullUp, 1); // P4_6 TARGET_MAX32625PICO TARGET_MAX40108DEMOP2U9 D14
#endif
#if HAS_digitalInOut15
    DigitalInOut digitalInOut15(P4_7, PIN_INPUT, PullUp, 1); // P4_7 TARGET_MAX32625PICO TARGET_MAX40108DEMOP2U9 D15
#endif
#if HAS_digitalInOut16
    // Ensure that the unused I2C pins do not interfere with analog inputs A4 and A5
    // DigitalInOut mode can be one of PullUp, PullDown, PullNone, OpenDrain
    DigitalInOut digitalInOut16(P1_6, PIN_INPUT, OpenDrain, 1); // P1_6 TARGET_MAX40108DEMOP2U9 J91.2=P1_6(button3/'%B3!'/SDA)
#endif
#if HAS_digitalInOut17
    // Ensure that the unused I2C pins do not interfere with analog inputs A4 and A5
    DigitalInOut digitalInOut17(P1_7, PIN_INPUT, OpenDrain, 1); // P1_7 TARGET_MAX40108DEMOP2U9 J91.3=P1_7(button2/'%B2!'/SCL)
#endif
//--------------------------------------------------
#elif defined(TARGET_NUCLEO_F446RE) || defined(TARGET_NUCLEO_F401RE)
    #define HAS_digitalInOut0 0
    #define HAS_digitalInOut1 0
#if APPLICATION_MAX11131
    // D2 -- MAX11131 EOC DigitalIn
    #define HAS_digitalInOut2 0
#else
    #define HAS_digitalInOut2 1
#endif
    #define HAS_digitalInOut3 1
    #define HAS_digitalInOut4 1
    #define HAS_digitalInOut5 1
    #define HAS_digitalInOut6 1
    #define HAS_digitalInOut7 1
#if APPLICATION_MAX5715
    // D8 -- MAX5715 CLRb DigitalOut
    #define HAS_digitalInOut8 0
#else
    #define HAS_digitalInOut8 1
#endif
#if APPLICATION_MAX5715
    // D9 -- MAX5715 LDACb DigitalOut
    #define HAS_digitalInOut9 0
#elif APPLICATION_MAX11131
    // D9 -- MAX11131 CNVST DigitalOut
    #define HAS_digitalInOut9 0
#else
    #define HAS_digitalInOut9 1
#endif
#if HAS_SPI
// avoid resource conflict between P5_0, P5_1, P5_2 SPI and DigitalInOut
    // Arduino digital pin D10 SPI function is CS/10
    // Arduino digital pin D11 SPI function is MOSI/11
    // Arduino digital pin D12 SPI function is MISO/12
    // Arduino digital pin D13 SPI function is SCK/13
    #define HAS_digitalInOut10 0
    #define HAS_digitalInOut11 0
    #define HAS_digitalInOut12 0
    #define HAS_digitalInOut13 0
#else // HAS_SPI
    #define HAS_digitalInOut10 1
    #define HAS_digitalInOut11 1
    #define HAS_digitalInOut12 1
    #define HAS_digitalInOut13 1
#endif // HAS_SPI
#if HAS_I2C
// avoid resource conflict between P5_7, P6_0 I2C and DigitalInOut
    // Arduino digital pin D14 I2C function is A4/SDA (10pin digital connector)
    // Arduino digital pin D15 I2C function is A5/SCL (10pin digital connector)
    // Arduino digital pin D16 I2C function is A4/SDA (6pin analog connector)
    // Arduino digital pin D17 I2C function is A5/SCL (6pin analog connector)
    #define HAS_digitalInOut14 0
    #define HAS_digitalInOut15 0
    #define HAS_digitalInOut16 0
    #define HAS_digitalInOut17 0
#else // HAS_I2C
    #define HAS_digitalInOut14 1
    #define HAS_digitalInOut15 1
    #define HAS_digitalInOut16 0
    #define HAS_digitalInOut17 0
#endif // HAS_I2C
#if HAS_digitalInOut0
    DigitalInOut digitalInOut0(D0, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut1
    DigitalInOut digitalInOut1(D1, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut2
    DigitalInOut digitalInOut2(D2, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut3
    DigitalInOut digitalInOut3(D3, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut4
    DigitalInOut digitalInOut4(D4, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut5
    DigitalInOut digitalInOut5(D5, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut6
    DigitalInOut digitalInOut6(D6, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut7
    DigitalInOut digitalInOut7(D7, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut8
    DigitalInOut digitalInOut8(D8, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut9
    DigitalInOut digitalInOut9(D9, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut10
    // Arduino digital pin D10 SPI function is CS/10
    DigitalInOut digitalInOut10(D10, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut11
    // Arduino digital pin D11 SPI function is MOSI/11
    DigitalInOut digitalInOut11(D11, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut12
    // Arduino digital pin D12 SPI function is MISO/12
    DigitalInOut digitalInOut12(D12, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut13
    // Arduino digital pin D13 SPI function is SCK/13
    DigitalInOut digitalInOut13(D13, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut14
    // Arduino digital pin D14 I2C function is A4/SDA (10pin digital connector)
    DigitalInOut digitalInOut14(D14, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut15
    // Arduino digital pin D15 I2C function is A5/SCL (10pin digital connector)
    DigitalInOut digitalInOut15(D15, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut16
    // Arduino digital pin D16 I2C function is A4/SDA (6pin analog connector)
    DigitalInOut digitalInOut16(D16, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut17
    // Arduino digital pin D17 I2C function is A5/SCL (6pin analog connector)
    DigitalInOut digitalInOut17(D17, PIN_INPUT, PullUp, 1);
#endif
//--------------------------------------------------
#elif defined(TARGET_LPC1768)
    #define HAS_digitalInOut0 1
    #define HAS_digitalInOut1 1
    #define HAS_digitalInOut2 1
    #define HAS_digitalInOut3 1
    #define HAS_digitalInOut4 1
    #define HAS_digitalInOut5 1
    #define HAS_digitalInOut6 1
    #define HAS_digitalInOut7 1
    #define HAS_digitalInOut8 1
    #define HAS_digitalInOut9 1
//    #define HAS_digitalInOut10 1
//    #define HAS_digitalInOut11 1
//    #define HAS_digitalInOut12 1
//    #define HAS_digitalInOut13 1
//    #define HAS_digitalInOut14 1
//    #define HAS_digitalInOut15 1
#if HAS_digitalInOut0
    DigitalInOut digitalInOut0(p5, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.9/I2STX_SDA/MOSI1/MAT2.3
#endif
#if HAS_digitalInOut1
    DigitalInOut digitalInOut1(p6, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.8/I2STX_WS/MISO1/MAT2.2
#endif
#if HAS_digitalInOut2
    DigitalInOut digitalInOut2(p7, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.7/I2STX_CLK/SCK1/MAT2.1
#endif
#if HAS_digitalInOut3
    DigitalInOut digitalInOut3(p8, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.6/I2SRX_SDA/SSEL1/MAT2.0
#endif
#if HAS_digitalInOut4
    DigitalInOut digitalInOut4(p9, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.0/CAN_RX1/TXD3/SDA1
#endif
#if HAS_digitalInOut5
    DigitalInOut digitalInOut5(p10, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.1/CAN_TX1/RXD3/SCL1
#endif
#if HAS_digitalInOut6
    DigitalInOut digitalInOut6(p11, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.18/DCD1/MOSI0/MOSI1
#endif
#if HAS_digitalInOut7
    DigitalInOut digitalInOut7(p12, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.17/CTS1/MISO0/MISO
#endif
#if HAS_digitalInOut8
    DigitalInOut digitalInOut8(p13, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.15/TXD1/SCK0/SCK
#endif
#if HAS_digitalInOut9
    DigitalInOut digitalInOut9(p14, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.16/RXD1/SSEL0/SSEL
#endif
    //
    // these pins support analog input analogIn0 .. analogIn5
    //DigitalInOut digitalInOut_(p15, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.23/AD0.0/I2SRX_CLK/CAP3.0
    //DigitalInOut digitalInOut_(p16, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.24/AD0.1/I2SRX_WS/CAP3.1
    //DigitalInOut digitalInOut_(p17, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.25/AD0.2/I2SRX_SDA/TXD3
    //DigitalInOut digitalInOut_(p18, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.26/AD0.3/AOUT/RXD3
    //DigitalInOut digitalInOut_(p19, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P1.30/VBUS/AD0.4
    //DigitalInOut digitalInOut_(p20, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P1.31/SCK1/AD0.5
    //
    // these pins support PWM pwmDriver1 .. pwmDriver6
    //DigitalInOut digitalInOut_(p21, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P2.5/PWM1.6/DTR1/TRACEDATA0
    //DigitalInOut digitalInOut_(p22, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P2.4/PWM1.5/DSR1/TRACEDATA1
    //DigitalInOut digitalInOut_(p23, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P2.3/PWM1.4/DCD1/TRACEDATA2
    //DigitalInOut digitalInOut_(p24, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P2.2/PWM1.3/CTS1/TRACEDATA3
    //DigitalInOut digitalInOut_(p25, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P2.1/PWM1.2/RXD1
    //DigitalInOut digitalInOut_(p26, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P2.0/PWM1.1/TXD1/TRACECLK
    //
    // these could be additional digitalInOut pins
#if HAS_digitalInOut10
    DigitalInOut digitalInOut10(p27, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.11/RXD2/SCL2/MAT3.1
#endif
#if HAS_digitalInOut11
    DigitalInOut digitalInOut11(p28, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.10/TXD2/SDA2/MAT3.0
#endif
#if HAS_digitalInOut12
    DigitalInOut digitalInOut12(p29, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.5/I2SRX_WS/CAN_TX2/CAP2.1
#endif
#if HAS_digitalInOut13
    DigitalInOut digitalInOut13(p30, PIN_INPUT, PullUp, 1); // TARGET_LPC1768 P0.4/I2SRX_CLK/CAN_RX2/CAP2.0
#endif
#if HAS_digitalInOut14
    DigitalInOut digitalInOut14(___, PIN_INPUT, PullUp, 1);
#endif
#if HAS_digitalInOut15
    DigitalInOut digitalInOut15(___, PIN_INPUT, PullUp, 1);
#endif
#else
    // unknown target
#endif
// uncrustify-0.66.1 *INDENT-ON*
#if HAS_digitalInOut0 || HAS_digitalInOut1 \
    || HAS_digitalInOut2 || HAS_digitalInOut3 \
    || HAS_digitalInOut4 || HAS_digitalInOut5 \
    || HAS_digitalInOut6 || HAS_digitalInOut7 \
    || HAS_digitalInOut8 || HAS_digitalInOut9 \
    || HAS_digitalInOut10 || HAS_digitalInOut11 \
    || HAS_digitalInOut12 || HAS_digitalInOut13 \
    || HAS_digitalInOut14 || HAS_digitalInOut15 \
    || HAS_digitalInOut16 || HAS_digitalInOut17
#define HAS_digitalInOuts 1
#else
#warning "Note: There are no digitalInOut resources defined"
#endif

// uncrustify-0.66.1 *INDENT-OFF*
//--------------------------------------------------
// Declare the AnalogIn driver
// Optional analogIn support. If there is only one it should be analogIn1.
// A) analog input
#if defined(TARGET_MAX32630)
    #define HAS_analogIn0 1
    #define HAS_analogIn1 1
    #define HAS_analogIn2 1
    #define HAS_analogIn3 1
    #define HAS_analogIn4 1
    #define HAS_analogIn5 1
    #define HAS_analogIn6 1
    #define HAS_analogIn7 1
    #define HAS_analogIn8 1
    #define HAS_analogIn9 1
//    #define HAS_analogIn10 0
//    #define HAS_analogIn11 0
//    #define HAS_analogIn12 0
//    #define HAS_analogIn13 0
//    #define HAS_analogIn14 0
//    #define HAS_analogIn15 0
#if HAS_analogIn0
    AnalogIn analogIn0(AIN_0); // TARGET_MAX32630 J1.5 AIN_0 = AIN0 pin       fullscale is 1.2V
#endif
#if HAS_analogIn1
    AnalogIn analogIn1(AIN_1); // TARGET_MAX32630 J1.6 AIN_1 = AIN1 pin       fullscale is 1.2V
#endif
#if HAS_analogIn2
    AnalogIn analogIn2(AIN_2); // TARGET_MAX32630 J1.7 AIN_2 = AIN2 pin       fullscale is 1.2V
#endif
#if HAS_analogIn3
    AnalogIn analogIn3(AIN_3); // TARGET_MAX32630 J1.8 AIN_3 = AIN3 pin       fullscale is 1.2V
#endif
#if HAS_analogIn4
    AnalogIn analogIn4(AIN_4); // TARGET_MAX32630 J1.5 AIN_4 = AIN0 / 5.0     fullscale is 6.0V
#endif
#if HAS_analogIn5
    AnalogIn analogIn5(AIN_5); // TARGET_MAX32630 J1.6 AIN_5 = AIN1 / 5.0     fullscale is 6.0V
#endif
#if HAS_analogIn6
    AnalogIn analogIn6(AIN_6); // TARGET_MAX32630 AIN_6 = VDDB / 4.0     fullscale is 4.8V
#endif
#if HAS_analogIn7
    AnalogIn analogIn7(AIN_7); // TARGET_MAX32630 AIN_7 = VDD18          fullscale is 1.2V
#endif
#if HAS_analogIn8
    AnalogIn analogIn8(AIN_8); // TARGET_MAX32630 AIN_8 = VDD12          fullscale is 1.2V
#endif
#if HAS_analogIn9
    AnalogIn analogIn9(AIN_9); // TARGET_MAX32630 AIN_9 = VRTC / 2.0     fullscale is 2.4V
#endif
#if HAS_analogIn10
    AnalogIn analogIn10(____); // TARGET_MAX32630 AIN_10 = x undefined?
#endif
#if HAS_analogIn11
    AnalogIn analogIn11(____); // TARGET_MAX32630 AIN_11 = VDDIO / 4.0   fullscale is 4.8V
#endif
#if HAS_analogIn12
    AnalogIn analogIn12(____); // TARGET_MAX32630 AIN_12 = VDDIOH / 4.0  fullscale is 4.8V
#endif
#if HAS_analogIn13
    AnalogIn analogIn13(____);
#endif
#if HAS_analogIn14
    AnalogIn analogIn14(____);
#endif
#if HAS_analogIn15
    AnalogIn analogIn15(____);
#endif
//--------------------------------------------------
#elif defined(TARGET_MAX32625MBED)
    #define HAS_analogIn0 1
    #define HAS_analogIn1 1
    #define HAS_analogIn2 1
    #define HAS_analogIn3 1
    #define HAS_analogIn4 1
    #define HAS_analogIn5 1
#if HAS_analogIn0
    AnalogIn analogIn0(AIN_0); // TARGET_MAX32630 J1.5 AIN_0 = AIN0 pin       fullscale is 1.2V
#endif
#if HAS_analogIn1
    AnalogIn analogIn1(AIN_1); // TARGET_MAX32630 J1.6 AIN_1 = AIN1 pin       fullscale is 1.2V
#endif
#if HAS_analogIn2
    AnalogIn analogIn2(AIN_2); // TARGET_MAX32630 J1.7 AIN_2 = AIN2 pin       fullscale is 1.2V
#endif
#if HAS_analogIn3
    AnalogIn analogIn3(AIN_3); // TARGET_MAX32630 J1.8 AIN_3 = AIN3 pin       fullscale is 1.2V
#endif
#if HAS_analogIn4
    AnalogIn analogIn4(AIN_4); // TARGET_MAX32630 J1.5 AIN_4 = AIN0 / 5.0     fullscale is 6.0V
#endif
#if HAS_analogIn5
    AnalogIn analogIn5(AIN_5); // TARGET_MAX32630 J1.6 AIN_5 = AIN1 / 5.0     fullscale is 6.0V
#endif
//--------------------------------------------------
#elif defined(TARGET_MAX32620FTHR)
#warning "TARGET_MAX32620FTHR not previously tested; need to verify analogIn0..."
    #define HAS_analogIn0 1
    #define HAS_analogIn1 1
    #define HAS_analogIn2 1
    #define HAS_analogIn3 1
    #define HAS_analogIn4 1
    #define HAS_analogIn5 1
    #define HAS_analogIn6 1
    #define HAS_analogIn7 1
    #define HAS_analogIn8 1
    #define HAS_analogIn9 1
//    #define HAS_analogIn10 0
//    #define HAS_analogIn11 0
//    #define HAS_analogIn12 0
//    #define HAS_analogIn13 0
//    #define HAS_analogIn14 0
//    #define HAS_analogIn15 0
#if HAS_analogIn0
    AnalogIn analogIn0(AIN_0); // TARGET_MAX32620FTHR J1.5 AIN_0 = AIN0 pin       fullscale is 1.2V
#endif
#if HAS_analogIn1
    AnalogIn analogIn1(AIN_1); // TARGET_MAX32620FTHR J1.6 AIN_1 = AIN1 pin       fullscale is 1.2V
#endif
#if HAS_analogIn2
    AnalogIn analogIn2(AIN_2); // TARGET_MAX32620FTHR J1.7 AIN_2 = AIN2 pin       fullscale is 1.2V
#endif
#if HAS_analogIn3
    AnalogIn analogIn3(AIN_3); // TARGET_MAX32620FTHR J1.8 AIN_3 = AIN3 pin       fullscale is 1.2V
#endif
#if HAS_analogIn4
    AnalogIn analogIn4(AIN_4); // TARGET_MAX32620FTHR J1.5 AIN_4 = AIN0 / 5.0     fullscale is 6.0V
#endif
#if HAS_analogIn5
    AnalogIn analogIn5(AIN_5); // TARGET_MAX32620FTHR J1.6 AIN_5 = AIN1 / 5.0     fullscale is 6.0V
#endif
#if HAS_analogIn6
    AnalogIn analogIn6(AIN_6); // TARGET_MAX32620FTHR AIN_6 = VDDB / 4.0     fullscale is 4.8V
#endif
#if HAS_analogIn7
    AnalogIn analogIn7(AIN_7); // TARGET_MAX32620FTHR AIN_7 = VDD18          fullscale is 1.2V
#endif
#if HAS_analogIn8
    AnalogIn analogIn8(AIN_8); // TARGET_MAX32620FTHR AIN_8 = VDD12          fullscale is 1.2V
#endif
#if HAS_analogIn9
    AnalogIn analogIn9(AIN_9); // TARGET_MAX32620FTHR AIN_9 = VRTC / 2.0     fullscale is 2.4V
#endif
#if HAS_analogIn10
    AnalogIn analogIn10(____); // TARGET_MAX32620FTHR AIN_10 = x undefined?
#endif
#if HAS_analogIn11
    AnalogIn analogIn11(____); // TARGET_MAX32620FTHR AIN_11 = VDDIO / 4.0   fullscale is 4.8V
#endif
#if HAS_analogIn12
    AnalogIn analogIn12(____); // TARGET_MAX32620FTHR AIN_12 = VDDIOH / 4.0  fullscale is 4.8V
#endif
#if HAS_analogIn13
    AnalogIn analogIn13(____);
#endif
#if HAS_analogIn14
    AnalogIn analogIn14(____);
#endif
#if HAS_analogIn15
    AnalogIn analogIn15(____);
#endif
//--------------------------------------------------
#elif defined(TARGET_MAX32625PICO) || defined(TARGET_MAX40108DEMOP2U9)
// #warning "TARGET_MAX32625PICO not previously tested; need to verify analogIn0..."
    #define HAS_analogIn0 1
    #define HAS_analogIn1 1
    #define HAS_analogIn2 1
    #define HAS_analogIn3 1
    #define HAS_analogIn4 1
    #define HAS_analogIn5 1
#if HAS_analogIn0
    AnalogIn analogIn0(AIN_0); // TARGET_MAX32630 J1.5 AIN_0 = AIN0 pin       fullscale is 1.2V
#endif
#if HAS_analogIn1
    AnalogIn analogIn1(AIN_1); // TARGET_MAX32630 J1.6 AIN_1 = AIN1 pin       fullscale is 1.2V
#endif
#if HAS_analogIn2
    AnalogIn analogIn2(AIN_2); // TARGET_MAX32630 J1.7 AIN_2 = AIN2 pin       fullscale is 1.2V
#endif
#if HAS_analogIn3
    AnalogIn analogIn3(AIN_3); // TARGET_MAX32630 J1.8 AIN_3 = AIN3 pin       fullscale is 1.2V
#endif
#if HAS_analogIn4
    AnalogIn analogIn4(AIN_4); // TARGET_MAX32630 J1.5 AIN_4 = AIN0 / 5.0     fullscale is 6.0V
#endif
#if HAS_analogIn5
    AnalogIn analogIn5(AIN_5); // TARGET_MAX32630 J1.6 AIN_5 = AIN1 / 5.0     fullscale is 6.0V
#endif
//--------------------------------------------------
#elif defined(TARGET_MAX32600)
    #define HAS_analogIn0 1
    #define HAS_analogIn1 1
    #define HAS_analogIn2 1
    #define HAS_analogIn3 1
    #define HAS_analogIn4 1
    #define HAS_analogIn5 1
#if HAS_analogIn0
    AnalogIn analogIn0(A0);
#endif
#if HAS_analogIn1
    AnalogIn analogIn1(A1);
#endif
#if HAS_analogIn2
    AnalogIn analogIn2(A2);
#endif
#if HAS_analogIn3
    AnalogIn analogIn3(A3);
#endif
#if HAS_analogIn4
    AnalogIn analogIn4(A4);
#endif
#if HAS_analogIn5
    AnalogIn analogIn5(A5);
#endif
//--------------------------------------------------
#elif defined(TARGET_NUCLEO_F446RE)
    #define HAS_analogIn0 1
    #define HAS_analogIn1 1
    #define HAS_analogIn2 1
    #define HAS_analogIn3 1
    #define HAS_analogIn4 1
    #define HAS_analogIn5 1
#if HAS_analogIn0
    AnalogIn analogIn0(A0);
#endif
#if HAS_analogIn1
    AnalogIn analogIn1(A1);
#endif
#if HAS_analogIn2
    AnalogIn analogIn2(A2);
#endif
#if HAS_analogIn3
    AnalogIn analogIn3(A3);
#endif
#if HAS_analogIn4
    AnalogIn analogIn4(A4);
#endif
#if HAS_analogIn5
    AnalogIn analogIn5(A5);
#endif
//--------------------------------------------------
#elif defined(TARGET_NUCLEO_F401RE)
    #define HAS_analogIn0 1
    #define HAS_analogIn1 1
    #define HAS_analogIn2 1
    #define HAS_analogIn3 1
    #define HAS_analogIn4 1
    #define HAS_analogIn5 1
#if HAS_analogIn0
    AnalogIn analogIn0(A0);
#endif
#if HAS_analogIn1
    AnalogIn analogIn1(A1);
#endif
#if HAS_analogIn2
    AnalogIn analogIn2(A2);
#endif
#if HAS_analogIn3
    AnalogIn analogIn3(A3);
#endif
#if HAS_analogIn4
    AnalogIn analogIn4(A4);
#endif
#if HAS_analogIn5
    AnalogIn analogIn5(A5);
#endif
//--------------------------------------------------
// TODO1: TARGET=MAX32625MBED ARM Cortex-M4F 96MHz 512kB Flash 160kB SRAM
#elif defined(TARGET_LPC1768)
    #define HAS_analogIn0 1
    #define HAS_analogIn1 1
    #define HAS_analogIn2 1
    #define HAS_analogIn3 1
    #define HAS_analogIn4 1
    #define HAS_analogIn5 1
//    #define HAS_analogIn6 1
//    #define HAS_analogIn7 1
//    #define HAS_analogIn8 1
//    #define HAS_analogIn9 1
//    #define HAS_analogIn10 1
//    #define HAS_analogIn11 1
//    #define HAS_analogIn12 1
//    #define HAS_analogIn13 1
//    #define HAS_analogIn14 1
//    #define HAS_analogIn15 1
#if HAS_analogIn0
    AnalogIn analogIn0(p15); // TARGET_LPC1768 P0.23/AD0.0/I2SRX_CLK/CAP3.0
#endif
#if HAS_analogIn1
    AnalogIn analogIn1(p16); // TARGET_LPC1768 P0.24/AD0.1/I2SRX_WS/CAP3.1
#endif
#if HAS_analogIn2
    AnalogIn analogIn2(p17); // TARGET_LPC1768 P0.25/AD0.2/I2SRX_SDA/TXD3
#endif
#if HAS_analogIn3
    AnalogIn analogIn3(p18); // TARGET_LPC1768 P0.26/AD0.3/AOUT/RXD3
#endif
#if HAS_analogIn4
    AnalogIn analogIn4(p19); // TARGET_LPC1768 P1.30/VBUS/AD0.4
#endif
#if HAS_analogIn5
    AnalogIn analogIn5(p20); // TARGET_LPC1768 P1.31/SCK1/AD0.5
#endif
#if HAS_analogIn6
    AnalogIn analogIn6(____);
#endif
#if HAS_analogIn7
    AnalogIn analogIn7(____);
#endif
#if HAS_analogIn8
    AnalogIn analogIn8(____);
#endif
#if HAS_analogIn9
    AnalogIn analogIn9(____);
#endif
#if HAS_analogIn10
    AnalogIn analogIn10(____);
#endif
#if HAS_analogIn11
    AnalogIn analogIn11(____);
#endif
#if HAS_analogIn12
    AnalogIn analogIn12(____);
#endif
#if HAS_analogIn13
    AnalogIn analogIn13(____);
#endif
#if HAS_analogIn14
    AnalogIn analogIn14(____);
#endif
#if HAS_analogIn15
    AnalogIn analogIn15(____);
#endif
#else
    // unknown target
#endif
// uncrustify-0.66.1 *INDENT-ON*
#if HAS_analogIn0 || HAS_analogIn1 \
    || HAS_analogIn2 || HAS_analogIn3 \
    || HAS_analogIn4 || HAS_analogIn5 \
    || HAS_analogIn6 || HAS_analogIn7 \
    || HAS_analogIn8 || HAS_analogIn9 \
    || HAS_analogIn10 || HAS_analogIn11 \
    || HAS_analogIn12 || HAS_analogIn13 \
    || HAS_analogIn14 || HAS_analogIn15
#define HAS_analogIns 1
#else
#warning "Note: There are no analogIn resources defined"
#endif

// DigitalInOut pin resource: print the pin index names to serial
#if HAS_digitalInOuts
void list_digitalInOutPins(Stream& serialStream)
{
#if HAS_digitalInOut0
    serialStream.printf(" 0");
#endif
#if HAS_digitalInOut1
    serialStream.printf(" 1");
#endif
#if HAS_digitalInOut2
    serialStream.printf(" 2");
#endif
#if HAS_digitalInOut3
    serialStream.printf(" 3");
#endif
#if HAS_digitalInOut4
    serialStream.printf(" 4");
#endif
#if HAS_digitalInOut5
    serialStream.printf(" 5");
#endif
#if HAS_digitalInOut6
    serialStream.printf(" 6");
#endif
#if HAS_digitalInOut7
    serialStream.printf(" 7");
#endif
#if HAS_digitalInOut8
    serialStream.printf(" 8");
#endif
#if HAS_digitalInOut9
    serialStream.printf(" 9");
#endif
#if HAS_digitalInOut10
    serialStream.printf(" 10");
#endif
#if HAS_digitalInOut11
    serialStream.printf(" 11");
#endif
#if HAS_digitalInOut12
    serialStream.printf(" 12");
#endif
#if HAS_digitalInOut13
    serialStream.printf(" 13");
#endif
#if HAS_digitalInOut14
    serialStream.printf(" 14");
#endif
#if HAS_digitalInOut15
    serialStream.printf(" 15");
#endif
#if HAS_digitalInOut16
    serialStream.printf(" 16");
#endif
#if HAS_digitalInOut17
    serialStream.printf(" 17");
#endif
#if USE_LEDS
// support LEDs as digital pins 91 92 93; WIP button as digital pin 90
// support list_digitalInOutPins() listing buttons and leds as pin numbers 90/91/92/93
// TODO: support find_digitalInOutPin(90) return DigitalInOut of switch SW1/BUTTON1
    //~ serialStream.printf(" 90");
// support find_digitalInOutPin(91) return DigitalInOut of led1_RFailLED
    serialStream.printf(" 91");
// support find_digitalInOutPin(92) return DigitalInOut of led2_GPassLED
    serialStream.printf(" 92");
// support find_digitalInOutPin(93) return DigitalInOut of led3_BBusyLED
    serialStream.printf(" 93");
#else // USE_LEDS
#endif // USE_LEDS
}
#endif


// DigitalInOut pin resource: search index
#if HAS_digitalInOuts
DigitalInOut& find_digitalInOutPin(int cPinIndex)
{
    switch (cPinIndex)
    {
        default: // default to the first defined digitalInOut pin
#if HAS_digitalInOut0
        case '0': case 0x00: return digitalInOut0;
#endif
#if HAS_digitalInOut1
        case '1': case 0x01: return digitalInOut1;
#endif
#if HAS_digitalInOut2
        case '2': case 0x02: return digitalInOut2;
#endif
#if HAS_digitalInOut3
        case '3': case 0x03: return digitalInOut3;
#endif
#if HAS_digitalInOut4
        case '4': case 0x04: return digitalInOut4;
#endif
#if HAS_digitalInOut5
        case '5': case 0x05: return digitalInOut5;
#endif
#if HAS_digitalInOut6
        case '6': case 0x06: return digitalInOut6;
#endif
#if HAS_digitalInOut7
        case '7': case 0x07: return digitalInOut7;
#endif
#if HAS_digitalInOut8
        case '8': case 0x08: return digitalInOut8;
#endif
#if HAS_digitalInOut9
        case '9': case 0x09: return digitalInOut9;
#endif
#if HAS_digitalInOut10
        case 'a': case 0x0a: return digitalInOut10;
#endif
#if HAS_digitalInOut11
        case 'b': case 0x0b: return digitalInOut11;
#endif
#if HAS_digitalInOut12
        case 'c': case 0x0c: return digitalInOut12;
#endif
#if HAS_digitalInOut13
        case 'd': case 0x0d: return digitalInOut13;
#endif
#if HAS_digitalInOut14
        case 'e': case 0x0e: return digitalInOut14;
#endif
#if HAS_digitalInOut15
        case 'f': case 0x0f: return digitalInOut15;
#endif
#if HAS_digitalInOut16
        case 'g': case 0x10: return digitalInOut16;
#endif
#if HAS_digitalInOut17
        case 'h': case 0x11: return digitalInOut17;
#endif
// support LEDs as digital pins 91 92 93; WIP button as digital pin 90
// TODO: support find_digitalInOutPin(90) return DigitalInOut of switch SW1/BUTTON1
        //~ case 90: return button1;
#if USE_LEDS
// support find_digitalInOutPin(91) return DigitalInOut of led1_RFailLED
        case 91: return led1_RFailLED;
// support find_digitalInOutPin(92) return DigitalInOut of led2_GPassLED
        case 92: return led2_GPassLED;
// support find_digitalInOutPin(93) return DigitalInOut of led3_BBusyLED
        case 93: return led3_BBusyLED;
#else // USE_LEDS
#endif // USE_LEDS
    }
}
#endif


// AnalogIn pin resource: search index
#if HAS_analogIns
AnalogIn& find_analogInPin(int cPinIndex)
{
    switch (cPinIndex)
    {
        default: // default to the first defined analogIn pin
#if HAS_analogIn0
        case '0': case 0x00: return analogIn0;
#endif
#if HAS_analogIn1
        case '1': case 0x01: return analogIn1;
#endif
#if HAS_analogIn2
        case '2': case 0x02: return analogIn2;
#endif
#if HAS_analogIn3
        case '3': case 0x03: return analogIn3;
#endif
#if HAS_analogIn4
        case '4': case 0x04: return analogIn4;
#endif
#if HAS_analogIn5
        case '5': case 0x05: return analogIn5;
#endif
#if HAS_analogIn6
        case '6': case 0x06: return analogIn6;
#endif
#if HAS_analogIn7
        case '7': case 0x07: return analogIn7;
#endif
#if HAS_analogIn8
        case '8': case 0x08: return analogIn8;
#endif
#if HAS_analogIn9
        case '9': case 0x09: return analogIn9;
#endif
#if HAS_analogIn10
        case 'a': case 0x0a: return analogIn10;
#endif
#if HAS_analogIn11
        case 'b': case 0x0b: return analogIn11;
#endif
#if HAS_analogIn12
        case 'c': case 0x0c: return analogIn12;
#endif
#if HAS_analogIn13
        case 'd': case 0x0d: return analogIn13;
#endif
#if HAS_analogIn14
        case 'e': case 0x0e: return analogIn14;
#endif
#if HAS_analogIn15
        case 'f': case 0x0f: return analogIn15;
#endif
    }
}
#endif

#if HAS_analogIns
const float analogInPin_fullScaleVoltage[] = {
# if defined(TARGET_MAX32630)
    ADC_FULL_SCALE_VOLTAGE, // analogIn0
    ADC_FULL_SCALE_VOLTAGE, // analogIn1
    ADC_FULL_SCALE_VOLTAGE, // analogIn2
    ADC_FULL_SCALE_VOLTAGE, // analogIn3
    ADC_FULL_SCALE_VOLTAGE * 5.0f, // analogIn4 // AIN_4 = AIN0 / 5.0     fullscale is 6.0V
    ADC_FULL_SCALE_VOLTAGE * 5.0f, // analogIn4 // AIN_5 = AIN1 / 5.0     fullscale is 6.0V
    ADC_FULL_SCALE_VOLTAGE * 4.0f, // analogIn6 // AIN_6 = VDDB / 4.0     fullscale is 4.8V
    ADC_FULL_SCALE_VOLTAGE, // analogIn7 // AIN_7 = VDD18          fullscale is 1.2V
    ADC_FULL_SCALE_VOLTAGE, // analogIn8 // AIN_8 = VDD12          fullscale is 1.2V
    ADC_FULL_SCALE_VOLTAGE * 2.0f, // analogIn9 // AIN_9 = VRTC / 2.0     fullscale is 2.4V
    ADC_FULL_SCALE_VOLTAGE, // analogIn10  // AIN_10 = x undefined?
    ADC_FULL_SCALE_VOLTAGE * 4.0f, // analogIn11 // AIN_11 = VDDIO / 4.0   fullscale is 4.8V
    ADC_FULL_SCALE_VOLTAGE * 4.0f, // analogIn12 // AIN_12 = VDDIOH / 4.0  fullscale is 4.8V
    ADC_FULL_SCALE_VOLTAGE, // analogIn13
    ADC_FULL_SCALE_VOLTAGE, // analogIn14
    ADC_FULL_SCALE_VOLTAGE // analogIn15
# elif defined(TARGET_MAX32620FTHR)
#warning "TARGET_MAX32620FTHR not previously tested; need to verify analogIn0..."
    ADC_FULL_SCALE_VOLTAGE, // analogIn0
    ADC_FULL_SCALE_VOLTAGE, // analogIn1
    ADC_FULL_SCALE_VOLTAGE, // analogIn2
    ADC_FULL_SCALE_VOLTAGE, // analogIn3
    ADC_FULL_SCALE_VOLTAGE * 5.0f, // analogIn4 // AIN_4 = AIN0 / 5.0     fullscale is 6.0V
    ADC_FULL_SCALE_VOLTAGE * 5.0f, // analogIn4 // AIN_5 = AIN1 / 5.0     fullscale is 6.0V
    ADC_FULL_SCALE_VOLTAGE * 4.0f, // analogIn6 // AIN_6 = VDDB / 4.0     fullscale is 4.8V
    ADC_FULL_SCALE_VOLTAGE, // analogIn7 // AIN_7 = VDD18          fullscale is 1.2V
    ADC_FULL_SCALE_VOLTAGE, // analogIn8 // AIN_8 = VDD12          fullscale is 1.2V
    ADC_FULL_SCALE_VOLTAGE * 2.0f, // analogIn9 // AIN_9 = VRTC / 2.0     fullscale is 2.4V
    ADC_FULL_SCALE_VOLTAGE, // analogIn10  // AIN_10 = x undefined?
    ADC_FULL_SCALE_VOLTAGE * 4.0f, // analogIn11 // AIN_11 = VDDIO / 4.0   fullscale is 4.8V
    ADC_FULL_SCALE_VOLTAGE * 4.0f, // analogIn12 // AIN_12 = VDDIOH / 4.0  fullscale is 4.8V
    ADC_FULL_SCALE_VOLTAGE, // analogIn13
    ADC_FULL_SCALE_VOLTAGE, // analogIn14
    ADC_FULL_SCALE_VOLTAGE // analogIn15
#elif defined(TARGET_MAX32625MBED) || defined(TARGET_MAX32625PICO) || defined(TARGET_MAX40108DEMOP2U9)
    ADC_FULL_SCALE_VOLTAGE * 1.0f, // analogIn0 // fullscale is 1.2V
    ADC_FULL_SCALE_VOLTAGE * 1.0f, // analogIn1 // fullscale is 1.2V
    ADC_FULL_SCALE_VOLTAGE * 1.0f, // analogIn2 // fullscale is 1.2V
    ADC_FULL_SCALE_VOLTAGE * 1.0f, // analogIn3 // fullscale is 1.2V
    ADC_FULL_SCALE_VOLTAGE * 5.0f, // analogIn4 // AIN_4 = AIN0 / 5.0     fullscale is 6.0V
    ADC_FULL_SCALE_VOLTAGE * 5.0f, // analogIn4 // AIN_5 = AIN1 / 5.0     fullscale is 6.0V
    ADC_FULL_SCALE_VOLTAGE * 4.0f, // analogIn6 // AIN_6 = VDDB / 4.0     fullscale is 4.8V
    ADC_FULL_SCALE_VOLTAGE, // analogIn7 // AIN_7 = VDD18          fullscale is 1.2V
    ADC_FULL_SCALE_VOLTAGE, // analogIn8 // AIN_8 = VDD12          fullscale is 1.2V
    ADC_FULL_SCALE_VOLTAGE * 2.0f, // analogIn9 // AIN_9 = VRTC / 2.0     fullscale is 2.4V
    ADC_FULL_SCALE_VOLTAGE, // analogIn10  // AIN_10 = x undefined?
    ADC_FULL_SCALE_VOLTAGE * 4.0f, // analogIn11 // AIN_11 = VDDIO / 4.0   fullscale is 4.8V
    ADC_FULL_SCALE_VOLTAGE * 4.0f, // analogIn12 // AIN_12 = VDDIOH / 4.0  fullscale is 4.8V
    ADC_FULL_SCALE_VOLTAGE, // analogIn13
    ADC_FULL_SCALE_VOLTAGE, // analogIn14
    ADC_FULL_SCALE_VOLTAGE // analogIn15
#elif defined(TARGET_NUCLEO_F446RE)
    ADC_FULL_SCALE_VOLTAGE, // analogIn0
    ADC_FULL_SCALE_VOLTAGE, // analogIn1
    ADC_FULL_SCALE_VOLTAGE, // analogIn2
    ADC_FULL_SCALE_VOLTAGE, // analogIn3
    ADC_FULL_SCALE_VOLTAGE, // analogIn4
    ADC_FULL_SCALE_VOLTAGE, // analogIn5
    ADC_FULL_SCALE_VOLTAGE, // analogIn6
    ADC_FULL_SCALE_VOLTAGE, // analogIn7
    ADC_FULL_SCALE_VOLTAGE, // analogIn8
    ADC_FULL_SCALE_VOLTAGE, // analogIn9
    ADC_FULL_SCALE_VOLTAGE, // analogIn10
    ADC_FULL_SCALE_VOLTAGE, // analogIn11
    ADC_FULL_SCALE_VOLTAGE, // analogIn12
    ADC_FULL_SCALE_VOLTAGE, // analogIn13
    ADC_FULL_SCALE_VOLTAGE, // analogIn14
    ADC_FULL_SCALE_VOLTAGE // analogIn15
#elif defined(TARGET_NUCLEO_F401RE)
    ADC_FULL_SCALE_VOLTAGE, // analogIn0
    ADC_FULL_SCALE_VOLTAGE, // analogIn1
    ADC_FULL_SCALE_VOLTAGE, // analogIn2
    ADC_FULL_SCALE_VOLTAGE, // analogIn3
    ADC_FULL_SCALE_VOLTAGE, // analogIn4
    ADC_FULL_SCALE_VOLTAGE, // analogIn5
    ADC_FULL_SCALE_VOLTAGE, // analogIn6
    ADC_FULL_SCALE_VOLTAGE, // analogIn7
    ADC_FULL_SCALE_VOLTAGE, // analogIn8
    ADC_FULL_SCALE_VOLTAGE, // analogIn9
    ADC_FULL_SCALE_VOLTAGE, // analogIn10
    ADC_FULL_SCALE_VOLTAGE, // analogIn11
    ADC_FULL_SCALE_VOLTAGE, // analogIn12
    ADC_FULL_SCALE_VOLTAGE, // analogIn13
    ADC_FULL_SCALE_VOLTAGE, // analogIn14
    ADC_FULL_SCALE_VOLTAGE // analogIn15
//#elif defined(TARGET_LPC1768)
#else
    // unknown target
    ADC_FULL_SCALE_VOLTAGE, // analogIn0
    ADC_FULL_SCALE_VOLTAGE, // analogIn1
    ADC_FULL_SCALE_VOLTAGE, // analogIn2
    ADC_FULL_SCALE_VOLTAGE, // analogIn3
    ADC_FULL_SCALE_VOLTAGE, // analogIn4
    ADC_FULL_SCALE_VOLTAGE, // analogIn5
    ADC_FULL_SCALE_VOLTAGE, // analogIn6
    ADC_FULL_SCALE_VOLTAGE, // analogIn7
    ADC_FULL_SCALE_VOLTAGE, // analogIn8
    ADC_FULL_SCALE_VOLTAGE, // analogIn9
    ADC_FULL_SCALE_VOLTAGE, // analogIn10
    ADC_FULL_SCALE_VOLTAGE, // analogIn11
    ADC_FULL_SCALE_VOLTAGE, // analogIn12
    ADC_FULL_SCALE_VOLTAGE, // analogIn13
    ADC_FULL_SCALE_VOLTAGE, // analogIn14
    ADC_FULL_SCALE_VOLTAGE // analogIn15
# endif
};
#endif




//--------------------------------------------------
// TODO support CmdLine command menus (like on Serial_Tester)
#ifndef USE_CMDLINE_MENUS
#define USE_CMDLINE_MENUS 1
//~ #undef USE_CMDLINE_MENUS
#endif
#if USE_CMDLINE_MENUS // support CmdLine command menus
#include "CmdLine.h"
#endif // USE_CMDLINE_MENUS support CmdLine command menus
#if USE_CMDLINE_MENUS // support CmdLine command menus
extern CmdLine cmdLine; // declared later
#endif // USE_CMDLINE_MENUS support CmdLine command menus

//--------------------------------------------------
// support sleep modes in Datalogger_Trigger
#ifndef USE_DATALOGGER_SLEEP
#define USE_DATALOGGER_SLEEP 1
//~ #undef USE_DATALOGGER_SLEEP
#endif
#if USE_DATALOGGER_SLEEP // support sleep modes in Datalogger_Trigger
// TODO: USE_DATALOGGER_SLEEP -- support sleep modes in Datalogger_Trigger
#endif
#if USE_DATALOGGER_SLEEP // support sleep modes in Datalogger_Trigger
typedef enum Datalogger_Sleep_enum_t {
    datalogger_Sleep_LP0_Stop = 0,
    datalogger_Sleep_LP1_DeepSleep = 1,
    datalogger_Sleep_LP2_Sleep = 2,
    datalogger_Sleep_LP3_Run = 3,
} Datalogger_Sleep_enum_t;
// TODO: USE_DATALOGGER_SLEEP -- support sleep modes in Datalogger_Trigger
Datalogger_Sleep_enum_t g_timer_sleepmode  = datalogger_Sleep_LP3_Run;
#endif

//--------------------------------------------------
// Datalog trigger types
#ifndef USE_DATALOGGER_TRIGGER
#define USE_DATALOGGER_TRIGGER 1
//~ #undef USE_DATALOGGER_TRIGGER
#endif
#if USE_DATALOGGER_TRIGGER // support Datalog trigger
typedef enum Datalogger_Trigger_enum_t {
    trigger_Halt = 0,               //!< halt
    trigger_FreeRun = 1,            //!< free run as fast as possible
    trigger_Timer = 2,              //!< timer (configure interval)
    trigger_PlatformDigitalInput,   //!< platform digital input (configure digital input pin reference)
    trigger_SPIDeviceRegRead,       //!< SPI device register read (configure regaddr, mask value, match value)
} Datalogger_Trigger_enum_t;
Datalogger_Trigger_enum_t Datalogger_Trigger = trigger_FreeRun;
//
// configuration for trigger_Timer
int g_timer_interval_msec = 500; // trigger_Timer
int g_timer_interval_count = 10; // trigger_Timer
int g_timer_interval_counter = 0; // trigger_Timer
//
// TODO: configuration for trigger_PlatformDigitalInput
//
// TODO: configuration for trigger_SPIDeviceRegRead
//
#endif // USE_DATALOGGER_TRIGGER support Datalog trigger

//--------------------------------------------------
// support trigger_SPIDeviceRegRead: Datalog when SPI read of address matches mask
#ifndef USE_DATALOGGER_SPIDeviceRegRead
#define USE_DATALOGGER_SPIDeviceRegRead 0
#endif // USE_DATALOGGER_SPIDeviceRegRead
#if USE_DATALOGGER_SPIDeviceRegRead
// TODO: uint16_t regAddr;
// TODO: uint16_t regDataMask;
// TODO: uint16_t regDataTest;
#endif // USE_DATALOGGER_SPIDeviceRegRead

//--------------------------------------------------
// support Datalogger_PrintRow() print gstrRemarkText field from recent #remark
#ifndef USE_DATALOGGER_REMARK_FIELD
#define USE_DATALOGGER_REMARK_FIELD 1
#endif // USE_DATALOGGER_REMARK_FIELD
#if USE_DATALOGGER_REMARK_FIELD
// Datalogger_PrintRow() print gstrRemarkText field from recent #remark
const size_t gstrRemarkTextLASTINDEX = 40; // gstrRemarkText buffer size - 1
char gstrRemarkText[gstrRemarkTextLASTINDEX+1] = "";
char gstrRemarkHeader[] = "comment"; // comment or remark?
#endif // USE_DATALOGGER_REMARK_FIELD

//--------------------------------------------------
// Datalogger_RunActionTable() supported actions
#ifndef USE_DATALOGGER_ActionTable
#define USE_DATALOGGER_ActionTable 1
//~ #undef USE_DATALOGGER_ActionTable
#endif
#if USE_DATALOGGER_ActionTable // Datalogger_RunActionTable() supported actions
//
// Datalogger_RunActionTable() supported actions
typedef enum action_type_enum_t {
    action_noop = 0,                // no operation
    action_digitalOutLow = 1,     // pin = low if condition
    action_digitalOutHigh = 2,    // pin = high if condition
    action_button = 3,  // pin = button event index 1, 2, 3
    action_trigger_Halt = 4,
    action_trigger_FreeRun = 5,
    action_trigger_Timer = 6,
    action_trigger_PlatformDigitalInput = 7,
    action_trigger_SPIDeviceRegRead = 8,
} action_type_enum_t;
//
// Datalogger_RunActionTable() supported conditions 
typedef enum action_condition_enum_t {
    condition_always = 0,       // (       true         )
    condition_if_An_gt_threshold,  // (Platform_Voltage[channel] >  threhsold)
    condition_if_An_lt_threshold,  // (Platform_Voltage[channel] <  threhsold)
    condition_if_An_eq_threshold,  // (Platform_Voltage[channel] == threhsold)
    condition_if_An_ge_threshold,  // (Platform_Voltage[channel] >= threhsold)
    condition_if_An_le_threshold,  // (Platform_Voltage[channel] <= threhsold)
    condition_if_An_ne_threshold,  // (Platform_Voltage[channel] != threhsold)
    condition_if_AINn_gt_threshold,  // (SPI_AIN_Voltage[channel] >  threhsold)
    condition_if_AINn_lt_threshold,  // (SPI_AIN_Voltage[channel] <  threhsold)
    condition_if_AINn_eq_threshold,  // (SPI_AIN_Voltage[channel] == threhsold)
    condition_if_AINn_ge_threshold,  // (SPI_AIN_Voltage[channel] >= threhsold)
    condition_if_AINn_le_threshold,  // (SPI_AIN_Voltage[channel] <= threhsold)
    condition_if_AINn_ne_threshold,  // (SPI_AIN_Voltage[channel] != threhsold)
} condition_enum_t;
//
// Datalogger_RunActionTable() structure
typedef struct action_table_row_t {
    action_type_enum_t      action;
    int                     digitalOutPin;
    action_condition_enum_t condition;
    int                     condition_channel;
    double                  condition_threshold;
} action_table_row_t;
#if MAX40108_DEMO
# ifdef BOARD_SERIAL_NUMBER
// data unique to certain boards based on serial number
// channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE)
#  if         (BOARD_SERIAL_NUMBER) == 1
    #warning "(BOARD_SERIAL_NUMBER) == 1 -- logic uses A3(CE) instead of A2(WE)"
const int channel_WE = 3; // use channel_CE instead on proto board s/n 1 for diagnostics; easier to sweep values
#  elif       (BOARD_SERIAL_NUMBER) == 5
    #warning "(BOARD_SERIAL_NUMBER) == 5 -- logic uses A3(CE) instead of A2(WE)"
const int channel_WE = 3; // use channel_CE instead on proto board s/n 1 for diagnostics; easier to sweep values
#  else // BOARD_SERIAL_NUMBER data unique to certain boards based on serial number
const int channel_WE = 2;
#  endif
# endif // BOARD_SERIAL_NUMBER data unique to certain boards based on serial number
const double threshold_WE_0V5 = 0.5;
const double threshold_WE_0V6 = 0.6;
const double threshold_WE_0V7 = 0.7;
const int pin_LED_1 = 91; // support find_digitalInOutPin(91) return DigitalInOut of led1_RFailLED
const int pin_LED_2 = 92; // support find_digitalInOutPin(92) return DigitalInOut of led2_GPassLED
const int pin_LED_3 = 93; // support find_digitalInOutPin(93) return DigitalInOut of led3_BBusyLED
#endif
const int ACTION_TABLE_ROW_MAX = 20;
#if MAX40108_DEMO
bool Datalogger_action_table_enabled = true;
int Datalogger_action_table_row_count = 8; // assert (Datalogger_action_table_row_count <= ACTION_TABLE_ROW_MAX)
#else // MAX40108_DEMO
int Datalogger_action_table_row_count = 0;
#endif // MAX40108_DEMO
action_table_row_t Datalogger_action_table[ACTION_TABLE_ROW_MAX] = {
#if MAX40108_DEMO
    // LED indicator show (WE > 0.5V)
    // L@0 act=1 pin=92 if=1 ch=2 x=0.500000 -- digitalOutLow D92 if A2 > 0.500000
    {action_digitalOutLow, pin_LED_2, condition_if_An_gt_threshold, channel_WE, threshold_WE_0V5},
    //
    // LED indicator show (WE > 0.6V)
    // L@1 act=1 pin=93 if=1 ch=2 x=0.600000 -- digitalOutLow D93 if A2 > 0.600000
    {action_digitalOutLow, pin_LED_3, condition_if_An_gt_threshold, channel_WE, threshold_WE_0V6},
    //
    // switch to always-on mode `trigger_FreeRun` if (WE > 0.7V)
    // L@2 act=5 if=1 ch=2 x=0.700000 -- trigger_FreeRun if A2 > 0.700000
    {action_trigger_FreeRun, 0, condition_if_An_gt_threshold, channel_WE, threshold_WE_0V7},
    //
    // switch to intermittent-sleep-mode `trigger_Timer` if (WE < 0.6V)
    // L@3 act=6 if=2 ch=2 x=0.600000 -- trigger_Timer(10 x 500msec) if A2 < 0.600000
    {action_trigger_Timer, 0, condition_if_An_lt_threshold, channel_WE, threshold_WE_0V6},
    //
    // LED indicator show (`Datalogger_Trigger` == `trigger_FreeRun`)
    // {action_noop, 0, condition_always, 0, 0},
    // L@4 act=1 pin=91 if=1 ch=2 x=0.700000 -- digitalOutLow D91 if A2 > 0.700000
    {action_digitalOutLow, pin_LED_1, condition_if_An_gt_threshold, channel_WE, threshold_WE_0V7},
    //
    // L@5 act=2 pin=91 if=2 ch=2 x=0.600000 -- digitalOutHigh D91 if A2 < 0.600000
    {action_digitalOutHigh, pin_LED_1, condition_if_An_lt_threshold, channel_WE, threshold_WE_0V6},
    //
    // L@6 act=3 pin=4 if=1 ch=2 x=0.700000 -- button 4 event if A2 > 0.700000
    {action_button, 4, condition_if_An_gt_threshold, channel_WE, threshold_WE_0V7},
    //
    // L@7 act=3 pin=5 if=2 ch=2 x=0.600000 -- button 5 event if A2 < 0.600000
    {action_button, 5, condition_if_An_lt_threshold, channel_WE, threshold_WE_0V6},
    //
#endif // MAX40108_DEMO
    //
    {action_noop, 0, condition_always, 0, 0},
};
#endif // USE_DATALOGGER_ActionTable Datalogger_RunActionTable() supported actions

//--------------------------------------------------
// print column header banner for csv data columns
uint8_t Datalogger_Need_PrintHeader = true;
uint8_t need_reinit = true;
void Datalogger_PrintHeader(CmdLine& cmdLine);
void Datalogger_AcquireRow();
#if USE_DATALOGGER_ActionTable // Datalogger_RunActionTable() supported actions
// TODO: Datalogger_RunActionTable() between Datalogger_AcquireRow() and Datalogger_PrintRow()
void Datalogger_RunActionTable();
#endif // USE_DATALOGGER_ActionTable Datalogger_RunActionTable() supported actions
void Datalogger_PrintRow(CmdLine& cmdLine);

//--------------------------------------------------
// Option to validate SPI link by reading PART_ID register
#ifndef VERIFY_PART_ID_IN_LOOP
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
#define VERIFY_PART_ID_IN_LOOP 1
#else
#define VERIFY_PART_ID_IN_LOOP 0
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
#endif

//--------------------------------------------------
#define NUM_DUT_ANALOG_IN_CHANNELS 10

#if defined(SPI_ADC_DeviceName) // SPI connected ADC
// MAX11410 individual channels 1=LSB, 2=Volt, 0=Disabled
typedef enum SPI_AIN_Enable_t {
    SPI_AIN_Disable = 0,
    SPI_AIN_Enable_LSB = 1,
    SPI_AIN_Enable_Volt = 2,
} SPI_AIN_Enable_t;
uint8_t SPI_AIN_Enable_ch[NUM_DUT_ANALOG_IN_CHANNELS] = {
    SPI_AIN_Enable_LSB,  // AIN0 1=LSB
    SPI_AIN_Enable_LSB,  // AIN1 1=LSB
    SPI_AIN_Enable_LSB,  // AIN2 1=LSB
    SPI_AIN_Enable_LSB,  // AIN3 1=LSB
    SPI_AIN_Enable_LSB,  // AIN4 1=LSB
    SPI_AIN_Enable_LSB,  // AIN5 1=LSB
    SPI_AIN_Enable_LSB,  // AIN6 1=LSB
    SPI_AIN_Enable_LSB,  // AIN7 1=LSB
    SPI_AIN_Enable_LSB,  // AIN8 1=LSB
    SPI_AIN_Enable_LSB,  // AIN9 1=LSB
};
//
double SPI_AIN_Voltage[NUM_DUT_ANALOG_IN_CHANNELS];
// Optional custom per-channel header suffix
#ifndef HAS_SPI_AIN_customChannelHeader
#define HAS_SPI_AIN_customChannelHeader 0
#endif
#if HAS_SPI_AIN_customChannelHeader // Optional custom per-channel header suffix
const char* const SPI_AIN_customChannelHeader_ch[NUM_DUT_ANALOG_IN_CHANNELS] = {
    "", // MAX40108: AIN0_1V0_current_   0.591202*100/3.34 = 17.70065868263473mA
    "", // MAX40108: AIN1_1V0_voltage
    "WE", // MAX40108: AIN2_WE
    "CE", // MAX40108: AIN3_CE
    "*100/3.34=mA", // MAX40108: AIN4_*100/3.34=mA
    "CELL_VOLTAGE", // MAX40108: AIN5_CELL_VOLTAGE
};
#endif // HAS_SPI_AIN_customChannelHeader
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
//  ---------- Measure_Voltage_custom_props in Measure_Voltage @pre and in class properties ----------
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
// MAX11410 specific per-channel config register v_filter
uint8_t SPI_AIN_Cfg_v_filter_ch[NUM_DUT_ANALOG_IN_CHANNELS] = {
    0x34, // AIN0 @ v_filter=0x34
    0x34, // AIN1 @ v_filter=0x34
    0x34, // AIN2 @ v_filter=0x34
    0x34, // AIN3 @ v_filter=0x34
    0x34, // AIN4 @ v_filter=0x34
    0x34, // AIN5 @ v_filter=0x34
    0x34, // AIN6 @ v_filter=0x34
    0x34, // AIN7 @ v_filter=0x34
    0x34, // AIN8 @ v_filter=0x34
    0x34, // AIN9 @ v_filter=0x34
};
//
// MAX11410 specific per-channel config register v_ctrl
uint8_t SPI_AIN_Cfg_v_ctrl_ch[NUM_DUT_ANALOG_IN_CHANNELS] = {
    0x42, // AIN0 @ v_ctrl=0x42
    0x42, // AIN1 @ v_ctrl=0x42
    0x42, // AIN2 @ v_ctrl=0x42
    0x42, // AIN3 @ v_ctrl=0x42
    0x42, // AIN4 @ v_ctrl=0x42
    0x42, // AIN5 @ v_ctrl=0x42
    0x42, // AIN6 @ v_ctrl=0x42
    0x42, // AIN7 @ v_ctrl=0x42
    0x42, // AIN8 @ v_ctrl=0x42
    0x42, // AIN9 @ v_ctrl=0x42
};
//
// MAX11410 specific per-channel config register v_pga
uint8_t SPI_AIN_Cfg_v_pga_ch[NUM_DUT_ANALOG_IN_CHANNELS] = {
    0x00, // AIN0 @ v_pga=0x00
    0x00, // AIN1 @ v_pga=0x00
    0x00, // AIN2 @ v_pga=0x00
    0x00, // AIN3 @ v_pga=0x00
    0x00, // AIN4 @ v_pga=0x00
    0x00, // AIN5 @ v_pga=0x00
    0x00, // AIN6 @ v_pga=0x00
    0x00, // AIN7 @ v_pga=0x00
    0x00, // AIN8 @ v_pga=0x00
    0x00, // AIN9 @ v_pga=0x00
};
//
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
//  ---------- Measure_Voltage_custom_props ----------
//  ---------- CUSTOMIZED from MAX11410_Hello after g_MAX11410_device.Init() ----------
// filter register configuration in Measure_Voltage and Read_All_Voltages CONV_TYPE_01_Continuous
//~ const uint8_t custom_v_filter = 0x34; // @ v_filter=0x34 --*LINEF_11_SINC4          RATE_0100 |   60SPS
//~ const uint8_t custom_v_filter = 0x00; // @ v_filter=0x00 -- LINEF_00_50Hz_60Hz_FIR  RATE_0000 |  1.1SPS
//~ const uint8_t custom_v_filter = 0x01; // @ v_filter=0x01 -- LINEF_00_50Hz_60Hz_FIR  RATE_0001 |  2.1SPS
//~ const uint8_t custom_v_filter = 0x02; // @ v_filter=0x02 -- LINEF_00_50Hz_60Hz_FIR  RATE_0010 |  4.2SPS
//~ const uint8_t custom_v_filter = 0x03; // @ v_filter=0x03 -- LINEF_00_50Hz_60Hz_FIR  RATE_0011 |  8.4SPS
//~ const uint8_t custom_v_filter = 0x04; // @ v_filter=0x04 -- LINEF_00_50Hz_60Hz_FIR  RATE_0100 | 16.8SPS
//~ const uint8_t custom_v_filter = 0x10; // @ v_filter=0x10 -- LINEF_01_50Hz_FIR       RATE_0000 |  1.3SPS
//~ const uint8_t custom_v_filter = 0x11; // @ v_filter=0x11 -- LINEF_01_50Hz_FIR       RATE_0001 |  2.7SPS
//~ const uint8_t custom_v_filter = 0x12; // @ v_filter=0x12 -- LINEF_01_50Hz_FIR       RATE_0010 |  5.3SPS
//~ const uint8_t custom_v_filter = 0x13; // @ v_filter=0x13 -- LINEF_01_50Hz_FIR       RATE_0011 | 10.7SPS
//~ const uint8_t custom_v_filter = 0x14; // @ v_filter=0x14 -- LINEF_01_50Hz_FIR       RATE_0100 | 21.3SPS
//~ const uint8_t custom_v_filter = 0x15; // @ v_filter=0x15 -- LINEF_01_50Hz_FIR       RATE_0101 | 40.0SPS
//~ const uint8_t custom_v_filter = 0x20; // @ v_filter=0x20 -- LINEF_10_60Hz_FIR       RATE_0000 |   1.3SPS
//~ const uint8_t custom_v_filter = 0x21; // @ v_filter=0x21 -- LINEF_10_60Hz_FIR       RATE_0001 |   2.7SPS
//~ const uint8_t custom_v_filter = 0x22; // @ v_filter=0x22 -- LINEF_10_60Hz_FIR       RATE_0010 |   5.3SPS
//~ const uint8_t custom_v_filter = 0x23; // @ v_filter=0x23 -- LINEF_10_60Hz_FIR       RATE_0011 |  10.7SPS
//~ const uint8_t custom_v_filter = 0x24; // @ v_filter=0x24 -- LINEF_10_60Hz_FIR       RATE_0100 |  21.3SPS
//~ const uint8_t custom_v_filter = 0x25; // @ v_filter=0x25 -- LINEF_10_60Hz_FIR       RATE_0101 |  40.0SPS
//~ const uint8_t custom_v_filter = 0x30; // @ v_filter=0x30 -- LINEF_11_SINC4          RATE_0000 |    4SPS
//~ const uint8_t custom_v_filter = 0x31; // @ v_filter=0x31 -- LINEF_11_SINC4          RATE_0001 |   10SPS
//~ const uint8_t custom_v_filter = 0x32; // @ v_filter=0x32 -- LINEF_11_SINC4          RATE_0010 |   20SPS
//~ const uint8_t custom_v_filter = 0x33; // @ v_filter=0x33 -- LINEF_11_SINC4          RATE_0011 |   40SPS
//~ const uint8_t custom_v_filter = 0x34; // @ v_filter=0x34 --*LINEF_11_SINC4          RATE_0100 |   60SPS
//~ const uint8_t custom_v_filter = 0x35; // @ v_filter=0x35 -- LINEF_11_SINC4          RATE_0101 |  120SPS
//~ const uint8_t custom_v_filter = 0x36; // @ v_filter=0x36 -- LINEF_11_SINC4          RATE_0110 |  240SPS
//~ const uint8_t custom_v_filter = 0x37; // @ v_filter=0x37 -- LINEF_11_SINC4          RATE_0111 |  480SPS
//~ const uint8_t custom_v_filter = 0x38; // @ v_filter=0x38 -- LINEF_11_SINC4          RATE_1000 |  960SPS
//~ const uint8_t custom_v_filter = 0x39; // @ v_filter=0x39 -- LINEF_11_SINC4          RATE_1001 | 1920SPS
//  ---------- CUSTOMIZED from MAX11410_Hello ----------
//
//  ---------- CUSTOMIZED from MAX11410_Hello after g_MAX11410_device.Init() ----------
// pga register configuration in Measure_Voltage and Read_All_Voltages
//~ const uint8_t custom_v_pga = 0x00;    // @ v_pga=0x00 -- 0x00 SIG_PATH_00_BUFFERED
//~ const uint8_t custom_v_pga = 0x00;    // @ v_pga=0x00 -- 0x00 SIG_PATH_00_BUFFERED
//~ const uint8_t custom_v_pga = 0x10;    // @ v_pga=0x10 -- 0x10 SIG_PATH_01_BYPASS
//~ const uint8_t custom_v_pga = 0x20;    // @ v_pga=0x20 -- 0x20 SIG_PATH_10_PGA GAIN_000_1
//~ const uint8_t custom_v_pga = 0x21;    // @ v_pga=0x21 --*0x21 SIG_PATH_10_PGA GAIN_001_2
//~ const uint8_t custom_v_pga = 0x22;    // @ v_pga=0x22 -- 0x22 SIG_PATH_10_PGA GAIN_010_4
//~ const uint8_t custom_v_pga = 0x23;    // @ v_pga=0x23 -- 0x23 SIG_PATH_10_PGA GAIN_011_8
//~ const uint8_t custom_v_pga = 0x24;    // @ v_pga=0x24 -- 0x24 SIG_PATH_10_PGA GAIN_100_16
//~ const uint8_t custom_v_pga = 0x25;    // @ v_pga=0x25 -- 0x25 SIG_PATH_10_PGA GAIN_101_32
//~ const uint8_t custom_v_pga = 0x26;    // @ v_pga=0x26 -- 0x26 SIG_PATH_10_PGA GAIN_110_64
//~ const uint8_t custom_v_pga = 0x27;    // @ v_pga=0x27 -- 0x27 SIG_PATH_10_PGA GAIN_111_128
//  ---------- CUSTOMIZED from MAX11410_Hello ----------
//
//  ---------- CUSTOMIZED from MAX11410_Hello after g_MAX11410_device.Init() ----------
// ctrl register configuration in Measure_Voltage and Read_All_Voltages
//~ const uint8_t custom_v_ctrl = 0x42;   // @ v_ctrl=0x42 -- 0x40 unipolar, 0x02 REF_SEL_010_REF2P_REF2N
//~ const uint8_t custom_v_ctrl = 0x40;   // @ v_ctrl=0x40 -- 0x40 unipolar, 0x00 REF_SEL_000_AIN0_AIN1
//~ const uint8_t custom_v_ctrl = 0x44;   // @ v_ctrl=0x44 -- 0x40 unipolar, 0x04 REF_SEL_100_AIN0_AGND
//~ const uint8_t custom_v_ctrl = 0x58;   // @ v_ctrl=0x58 -- 0x40 unipolar, 0x00 REF_SEL_000_AIN0_AIN1, 0x18 refbuf
//
//~ const uint8_t custom_v_ctrl = 0x41;   // @ v_ctrl=0x41 -- 0x40 unipolar, 0x01 REF_SEL_001_REF1P_REF1N
//~ const uint8_t custom_v_ctrl = 0x45;   // @ v_ctrl=0x45 -- 0x40 unipolar, 0x05 REF_SEL_101_REF1P_AGND
//~ const uint8_t custom_v_ctrl = 0x59;   // @ v_ctrl=0x59 -- 0x40 unipolar, 0x01 REF_SEL_001_REF1P_REF1N, 0x18 refbuf
//
//~ const uint8_t custom_v_ctrl = 0x42;   // @ v_ctrl=0x42 -- 0x40 unipolar, 0x02 REF_SEL_010_REF2P_REF2N
//~ const uint8_t custom_v_ctrl = 0x46;   // @ v_ctrl=0x46 -- 0x40 unipolar, 0x06 REF_SEL_110_REF2P_AGND
//~ const uint8_t custom_v_ctrl = 0x22;   // @ v_ctrl=0x22 -- 0x20 bipolar offset binary, 0x02 REF_SEL_010_REF2P_REF2N
//~ const uint8_t custom_v_ctrl = 0x02;   // @ v_ctrl=0x02 -- 0x00 bipolar 2's complement, 0x02 REF_SEL_010_REF2P_REF2N
//
//~ const uint8_t custom_v_ctrl = 0x44;   // @ v_ctrl=0x44 -- 0x40 unipolar, 0x04 REF_SEL_100_AIN0_AGND
//~ const uint8_t custom_v_ctrl = 0x47;   // @ v_ctrl=0x47 -- 0x40 unipolar, 0x07 REF_SEL_111_AVDD_AGND
//~ const uint8_t custom_v_ctrl = 0x27;   // @ v_ctrl=0x27 -- 0x20 bipolar offset binary, 0x07 REF_SEL_111_AVDD_AGND
//~ const uint8_t custom_v_ctrl = 0x07;   // @ v_ctrl=0x07 -- 0x00 bipolar 2's complement, 0x07 REF_SEL_111_AVDD_AGND
//  ---------- CUSTOMIZED from MAX11410_Hello ----------

#if defined(SPI_ADC_DeviceName) // SPI connected ADC
// example code declare SPI interface (GPIO controlled CS)
#if defined(TARGET_MAX32625MBED)
SPI spi(SPI1_MOSI, SPI1_MISO, SPI1_SCK); // mosi, miso, sclk spi1 TARGET_MAX32625MBED: P1_1 P1_2 P1_0 Arduino 10-pin header D11 D12 D13
DigitalOut spi_cs(SPI1_SS); // TARGET_MAX32625MBED: P1_3 Arduino 10-pin header D10
#elif defined(TARGET_MAX32625PICO) || defined(TARGET_MAX40108DEMOP2U9)
#warning "TARGET_MAX32625PICO not previously tested; need to define pins..."
SPI spi(SPI0_MOSI, SPI0_MISO, SPI0_SCK); // mosi, miso, sclk spi1 TARGET_MAX32625PICO: pin P0_5 P0_6 P0_4
DigitalOut spi_cs(SPI0_SS); // TARGET_MAX32625PICO: pin P0_7
#elif defined(TARGET_MAX32600MBED)
SPI spi(SPI2_MOSI, SPI2_MISO, SPI2_SCK); // mosi, miso, sclk spi1 TARGET_MAX32600MBED: Arduino 10-pin header D11 D12 D13
DigitalOut spi_cs(SPI2_SS); // Generic: Arduino 10-pin header D10
#elif defined(TARGET_NUCLEO_F446RE) || defined(TARGET_NUCLEO_F401RE)
// TODO1: avoid resource conflict between P5_0, P5_1, P5_2 SPI and DigitalInOut
// void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel)
//
// TODO1: NUCLEO_F446RE SPI not working; CS and MOSI data looks OK but no SCLK clock pulses.
SPI spi(SPI_MOSI, SPI_MISO, SPI_SCK); // mosi, miso, sclk spi1 TARGET_NUCLEO_F446RE: Arduino 10-pin header D11 D12 D13
DigitalOut spi_cs(SPI_CS); // TARGET_NUCLEO_F446RE: PB_6 Arduino 10-pin header D10
//
#else
SPI spi(D11, D12, D13); // mosi, miso, sclk spi1 TARGET_MAX32600MBED: Arduino 10-pin header D11 D12 D13
DigitalOut spi_cs(D10); // Generic: Arduino 10-pin header D10
#endif

// example code declare GPIO interface pins
// example code declare device instance
MAX11410 g_MAX11410_device(spi, spi_cs, MAX11410::MAX11410_IC);
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC

//--------------------------------------------------
// Option to Datalog Arduino platform analog inputs
#ifndef LOG_PLATFORM_AIN
#define LOG_PLATFORM_AIN 6
//~ #undef LOG_PLATFORM_AIN
#endif
#if defined(LOG_PLATFORM_AIN) // Datalog Arduino platform analog inputs
//#ifndef NUM_PLATFORM_ANALOG_IN_CHANNELS
//#define NUM_PLATFORM_ANALOG_IN_CHANNELS 6
//#endif
const int NUM_PLATFORM_ANALOG_IN_CHANNELS = 6;
const double adc_full_scale_voltage[NUM_PLATFORM_ANALOG_IN_CHANNELS] = {
    analogInPin_fullScaleVoltage[0], // 1.2,
    analogInPin_fullScaleVoltage[1], // 1.2,
    analogInPin_fullScaleVoltage[2], // 1.2,
    analogInPin_fullScaleVoltage[3], // 1.2,
    analogInPin_fullScaleVoltage[4], // 6.0
    analogInPin_fullScaleVoltage[5], // 6.0
};
// Platform ADC individual channels 1=LSB, 2=Volt, 0=Disabled
typedef enum Platform_AIN_Enable_t {
    Platform_AIN_Disable = 0,
    Platform_AIN_Enable_LSB = 1,
    Platform_AIN_Enable_Volt = 2,
} Platform_AIN_Enable_t;
uint8_t Platform_Enable_ch[NUM_PLATFORM_ANALOG_IN_CHANNELS] = {
    Platform_AIN_Enable_Volt,  // AIN0 2=Volt
    Platform_AIN_Enable_Volt,  // AIN1 2=Volt
    Platform_AIN_Enable_Volt,  // AIN2 2=Volt
    Platform_AIN_Enable_Volt,  // AIN3 2=Volt
    Platform_AIN_Enable_Volt,  // AIN4 2=Volt
    Platform_AIN_Enable_Volt,  // AIN5 2=Volt
};
// Option to report average value of Arduino platform analog inputs
#ifndef USE_Platform_AIN_Average
#define USE_Platform_AIN_Average 1
//~ #undef USE_Platform_AIN_Average
#endif
#if USE_Platform_AIN_Average
#endif // USE_Platform_AIN_Average
#if USE_Platform_AIN_Average
int Platform_AIN_Average_N = 16;
#endif // USE_Platform_AIN_Average
// Option to apply calibration to Arduino platform analog inputs
#ifndef HAS_Platform_AIN_Calibration
#define HAS_Platform_AIN_Calibration 1
//~ #undef HAS_Platform_AIN_Calibration
#endif
#if HAS_Platform_AIN_Calibration
double CalibratedNormValue(int channel_0_5, double raw_normValue_0_1);
// Calibration is between two points for each channel, defined by
// a normalized value between 0% and 100%, and the corresponding voltage.
// nominal 5% fullscale point; normValue_0_1 < 0.5
// calibration_05_normValue_0_1, calibration_05_V should be around 5% or 25%
double calibration_05_normValue_0_1 [NUM_PLATFORM_ANALOG_IN_CHANNELS] = {
#ifdef BOARD_SERIAL_NUMBER
// data unique to certain boards based on serial number
// channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE)
# if          (BOARD_SERIAL_NUMBER) == 0
    #warning "(BOARD_SERIAL_NUMBER) == 0"
    // TODO: calibrate BOARD_SERIAL_NUMBER 0 channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE)
    0.25, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.25, // %A cal1n=0.250000000 cal1v=0.300000000V cal1n=0.750000000 cal1v=0.900000000V
    0.25, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.25, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    0.25, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    0.25, // %A cal5n=0.250000000 cal5v=1.500000000V cal5n=0.750000000 cal5v=4.500000000V
    //
# elif        (BOARD_SERIAL_NUMBER) == 1
    #warning "(BOARD_SERIAL_NUMBER) == 1"
    // TODO: calibrate BOARD_SERIAL_NUMBER 1 channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE)
    0.250000000, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.624736547, // %A cal1n=0.624736547 cal1v=0.748000026V cal1n=0.979754724 cal1v=1.167500019V
    0.250000000, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.250000000, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    0.250000000, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    0.127252869, // %A cal5n=0.127252869 cal5v=0.748000026V cal5n=0.267205842 cal5v=1.578999996V
    //
# elif        (BOARD_SERIAL_NUMBER) == 2
    #warning "(BOARD_SERIAL_NUMBER) == 2"
    // TODO: calibrate BOARD_SERIAL_NUMBER 2 channels A1/A5(1V0)?
    0.001955034, // %A cal0n=0.001955034 cal0v=0.000120000V cal0n=0.964797903 cal0v=1.149270058V
    0.646371711, // %A cal1n=0.646371711 cal1v=0.761619985V cal1n=0.964797903 cal1v=1.149270058V
    0.121437412, // %A cal2n=0.121437412 cal2v=0.146500006V cal2n=0.833466958 cal2v=1.004999995V
    0.121437412, // %A cal3n=0.121437412 cal3v=0.146500006V cal3n=0.833466958 cal3v=1.004999995V
    0.001955034, // %A cal4n=0.001955034 cal4v=0.000120000V cal4n=0.291349728 cal4v=1.735399961V
    0.130005952, // %A cal5n=0.130005952 cal5v=0.761650026V cal5n=0.291349728 cal5v=1.735399961V
    //
# elif        (BOARD_SERIAL_NUMBER) == 3
    #warning "(BOARD_SERIAL_NUMBER) == 3"
    // TODO: calibrate BOARD_SERIAL_NUMBER 3 channels A2(WE), A3(CE) not quite right yet
    0.002932551, // %A cal0n=0.002932551 cal0v=0.000130000V cal0n=0.966447462 cal0v=1.149999976V
    0.643798094, // %A cal1n=0.643798094 cal1v=0.756900012V cal1n=0.966447462 cal1v=1.149999976V
    0.151286051, // %A cal2n=0.151286051 cal2v=0.181050003V cal2n=0.786534725 cal2v=0.936999977V
    0.151286051, // %A cal3n=0.151286051 cal3v=0.181050003V cal3n=0.786023080 cal3v=0.936999977V
    0.002932551, // %A cal4n=0.002932551 cal4v=0.000130000V cal4n=0.286962360 cal4v=1.707499981V
    0.130120505, // %A cal5n=0.130120505 cal5v=0.757099986V cal5n=0.286962360 cal5v=1.707499981V
    //
# elif        (BOARD_SERIAL_NUMBER) == 4
    #warning "(BOARD_SERIAL_NUMBER) == 4"
    0.003910068, // %A cal0n=0.003910068 cal0v=0.000120000V cal0n=0.925441444 cal0v=1.107100010V
    0.635619044, // %A cal1n=0.635619044 cal1v=0.759999990V cal1n=0.925441444 cal1v=1.107100010V
    0.083768636, // %A cal2n=0.083768636 cal2v=0.100000001V cal2n=0.820740156 cal2v=0.980000019V
    0.083768636, // %A cal3n=0.083768636 cal3v=0.100000001V cal3n=0.820740156 cal3v=0.980000019V
    0.003910068, // %A cal4n=0.003910068 cal4v=0.000120000V cal4n=0.279630989 cal4v=1.651999950V
    0.129257541, // %A cal5n=0.129257541 cal5v=0.759999990V cal5n=0.279630989 cal5v=1.651999950V
    //
# elif        (BOARD_SERIAL_NUMBER) == 5
    #warning "(BOARD_SERIAL_NUMBER) == 5"
    // TODO: calibrate BOARD_SERIAL_NUMBER 5 channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE) broken unit?
    0.25, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.25, // %A cal1n=0.250000000 cal1v=0.300000000V cal1n=0.750000000 cal1v=0.900000000V
    0.25, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.25, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    0.25, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    0.25, // %A cal5n=0.250000000 cal5v=1.500000000V cal5n=0.750000000 cal5v=4.500000000V
    //
# elif        (BOARD_SERIAL_NUMBER) == 6
    #warning "(BOARD_SERIAL_NUMBER) == 6"
    // TODO: calibrate BOARD_SERIAL_NUMBER 6 channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE)
    0.25, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.25, // %A cal1n=0.250000000 cal1v=0.300000000V cal1n=0.750000000 cal1v=0.900000000V
    0.25, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.25, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    0.25, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    0.25, // %A cal5n=0.250000000 cal5v=1.500000000V cal5n=0.750000000 cal5v=4.500000000V
    //
# else
    #warning "BOARD_SERIAL_NUMBER defined but not recognized"
# endif
#else // BOARD_SERIAL_NUMBER data unique to certain boards based on serial number
    #warning "BOARD_SERIAL_NUMBER not defined; using default values"
    0.25, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.25, // %A cal1n=0.250000000 cal1v=0.300000000V cal1n=0.750000000 cal1v=0.900000000V
    0.25, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.25, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    0.25, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    0.25, // %A cal5n=0.250000000 cal5v=1.500000000V cal5n=0.750000000 cal5v=4.500000000V
    //
#endif // BOARD_SERIAL_NUMBER data unique to certain boards based on serial number
};
double calibration_05_V [NUM_PLATFORM_ANALOG_IN_CHANNELS] = {
#ifdef BOARD_SERIAL_NUMBER
// data unique to certain boards based on serial number
# if          (BOARD_SERIAL_NUMBER) == 0
    #warning "(BOARD_SERIAL_NUMBER) == 0"
    // TODO: calibrate BOARD_SERIAL_NUMBER 0 channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE)
    0.3, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.3, // %A cal1n=0.250000000 cal1v=0.300000000V cal1n=0.750000000 cal1v=0.900000000V
    0.3, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.3, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    1.5, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    1.5, // %A cal5n=0.250000000 cal5v=1.500000000V cal5n=0.750000000 cal5v=4.500000000V
    //
# elif        (BOARD_SERIAL_NUMBER) == 1
    #warning "(BOARD_SERIAL_NUMBER) == 1"
    // TODO: calibrate BOARD_SERIAL_NUMBER 1 channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE)
    0.300000000, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.748000026, // %A cal1n=0.624736547 cal1v=0.748000026V cal1n=0.979754724 cal1v=1.167500019V
    0.300000000, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.300000000, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    1.500000000, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    0.748000026, // %A cal5n=0.127252869 cal5v=0.748000026V cal5n=0.267205842 cal5v=1.578999996V
    //
# elif        (BOARD_SERIAL_NUMBER) == 2
    #warning "(BOARD_SERIAL_NUMBER) == 2"
    // TODO: calibrate BOARD_SERIAL_NUMBER 2 channels A1/A5(1V0)?
    0.000120000, // %A cal0n=0.001955034 cal0v=0.000120000V cal0n=0.964797903 cal0v=1.149270058V
    0.761619985, // %A cal1n=0.646371711 cal1v=0.761619985V cal1n=0.964797903 cal1v=1.149270058V
    0.146500006, // %A cal2n=0.121437412 cal2v=0.146500006V cal2n=0.833466958 cal2v=1.004999995V
    0.146500006, // %A cal3n=0.121437412 cal3v=0.146500006V cal3n=0.833466958 cal3v=1.004999995V
    0.000120000, // %A cal4n=0.001955034 cal4v=0.000120000V cal4n=0.291349728 cal4v=1.735399961V
    0.761650026, // %A cal5n=0.130005952 cal5v=0.761650026V cal5n=0.291349728 cal5v=1.735399961V
    //
# elif        (BOARD_SERIAL_NUMBER) == 3
    #warning "(BOARD_SERIAL_NUMBER) == 3"
    // TODO: calibrate BOARD_SERIAL_NUMBER 3 channels A2(WE), A3(CE) not quite right yet
    0.000130000, // %A cal0n=0.002932551 cal0v=0.000130000V cal0n=0.966447462 cal0v=1.149999976V
    0.756900012, // %A cal1n=0.643798094 cal1v=0.756900012V cal1n=0.966447462 cal1v=1.149999976V
    0.181050003, // %A cal2n=0.151286051 cal2v=0.181050003V cal2n=0.786534725 cal2v=0.936999977V
    0.181050003, // %A cal3n=0.151286051 cal3v=0.181050003V cal3n=0.786023080 cal3v=0.936999977V
    0.000130000, // %A cal4n=0.002932551 cal4v=0.000130000V cal4n=0.286962360 cal4v=1.707499981V
    0.757099986, // %A cal5n=0.130120505 cal5v=0.757099986V cal5n=0.286962360 cal5v=1.707499981V
    //
# elif        (BOARD_SERIAL_NUMBER) == 4
    #warning "(BOARD_SERIAL_NUMBER) == 4"
    0.000120000, // %A cal0n=0.003910068 cal0v=0.000120000V cal0n=0.925441444 cal0v=1.107100010V
    0.759999990, // %A cal1n=0.635619044 cal1v=0.759999990V cal1n=0.925441444 cal1v=1.107100010V
    0.100000001, // %A cal2n=0.083768636 cal2v=0.100000001V cal2n=0.820740156 cal2v=0.980000019V
    0.100000001, // %A cal3n=0.083768636 cal3v=0.100000001V cal3n=0.820740156 cal3v=0.980000019V
    0.000120000, // %A cal4n=0.003910068 cal4v=0.000120000V cal4n=0.279630989 cal4v=1.651999950V
    0.759999990, // %A cal5n=0.129257541 cal5v=0.759999990V cal5n=0.279630989 cal5v=1.651999950V
    //
# elif        (BOARD_SERIAL_NUMBER) == 5
    #warning "(BOARD_SERIAL_NUMBER) == 5"
    // TODO: calibrate BOARD_SERIAL_NUMBER 5 channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE)
    0.3, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.3, // %A cal1n=0.250000000 cal1v=0.300000000V cal1n=0.750000000 cal1v=0.900000000V
    0.3, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.3, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    1.5, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    1.5, // %A cal5n=0.250000000 cal5v=1.500000000V cal5n=0.750000000 cal5v=4.500000000V
    //
# elif        (BOARD_SERIAL_NUMBER) == 6
    #warning "(BOARD_SERIAL_NUMBER) == 6"
    // TODO: calibrate BOARD_SERIAL_NUMBER 6 channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE)
    0.3, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.3, // %A cal1n=0.250000000 cal1v=0.300000000V cal1n=0.750000000 cal1v=0.900000000V
    0.3, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.3, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    1.5, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    1.5, // %A cal5n=0.250000000 cal5v=1.500000000V cal5n=0.750000000 cal5v=4.500000000V
    //
# else
    #warning "BOARD_SERIAL_NUMBER defined but not recognized"
# endif
#else // BOARD_SERIAL_NUMBER data unique to certain boards based on serial number
    #warning "BOARD_SERIAL_NUMBER not defined; using default values"
    0.3, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.3, // %A cal1n=0.250000000 cal1v=0.300000000V cal1n=0.750000000 cal1v=0.900000000V
    0.3, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.3, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    1.5, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    1.5, // %A cal5n=0.250000000 cal5v=1.500000000V cal5n=0.750000000 cal5v=4.500000000V
    //
#endif // BOARD_SERIAL_NUMBER data unique to certain boards based on serial number
};
// nominal 95% fullscale point; normValue_0_1 > 0.5
// calibration_95_normValue_0_1, calibration_95_V should be around 95% or 75%
double calibration_95_normValue_0_1 [NUM_PLATFORM_ANALOG_IN_CHANNELS] = {
#ifdef BOARD_SERIAL_NUMBER
// data unique to certain boards based on serial number
# if          (BOARD_SERIAL_NUMBER) == 0
    #warning "(BOARD_SERIAL_NUMBER) == 0"
    // TODO: calibrate BOARD_SERIAL_NUMBER 0 channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE)
    0.75, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.75, // %A cal1n=0.250000000 cal1v=0.300000000V cal1n=0.750000000 cal1v=0.900000000V
    0.75, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.75, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    0.75, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    0.75, // %A cal5n=0.250000000 cal5v=1.500000000V cal5n=0.750000000 cal5v=4.500000000V
    //
# elif        (BOARD_SERIAL_NUMBER) == 1
    #warning "(BOARD_SERIAL_NUMBER) == 1"
    // TODO: calibrate BOARD_SERIAL_NUMBER 1 channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE)
    0.750000000, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.979754724, // %A cal1n=0.624736547 cal1v=0.748000026V cal1n=0.979754724 cal1v=1.167500019V
    0.750000000, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.750000000, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    0.750000000, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    0.267205842, // %A cal5n=0.127252869 cal5v=0.748000026V cal5n=0.267205842 cal5v=1.578999996V
    //
# elif        (BOARD_SERIAL_NUMBER) == 2
    #warning "(BOARD_SERIAL_NUMBER) == 2"
    // TODO: calibrate BOARD_SERIAL_NUMBER 2 channels A1/A5(1V0)?
    0.964797903, // %A cal0n=0.001955034 cal0v=0.000120000V cal0n=0.964797903 cal0v=1.149270058V
    0.964797903, // %A cal1n=0.646371711 cal1v=0.761619985V cal1n=0.964797903 cal1v=1.149270058V
    0.833466958, // %A cal2n=0.121437412 cal2v=0.146500006V cal2n=0.833466958 cal2v=1.004999995V
    0.833466958, // %A cal3n=0.121437412 cal3v=0.146500006V cal3n=0.833466958 cal3v=1.004999995V
    0.291349728, // %A cal4n=0.001955034 cal4v=0.000120000V cal4n=0.291349728 cal4v=1.735399961V
    0.291349728, // %A cal5n=0.130005952 cal5v=0.761650026V cal5n=0.291349728 cal5v=1.735399961V
    //
# elif        (BOARD_SERIAL_NUMBER) == 3
    #warning "(BOARD_SERIAL_NUMBER) == 3"
    // TODO: calibrate BOARD_SERIAL_NUMBER 3 channels A2(WE), A3(CE) not quite right yet
    0.966447462, // %A cal0n=0.002932551 cal0v=0.000130000V cal0n=0.966447462 cal0v=1.149999976V
    0.966447462, // %A cal1n=0.643798094 cal1v=0.756900012V cal1n=0.966447462 cal1v=1.149999976V
    0.786534725, // %A cal2n=0.151286051 cal2v=0.181050003V cal2n=0.786534725 cal2v=0.936999977V
    0.786023080, // %A cal3n=0.151286051 cal3v=0.181050003V cal3n=0.786023080 cal3v=0.936999977V
    0.286962360, // %A cal4n=0.002932551 cal4v=0.000130000V cal4n=0.286962360 cal4v=1.707499981V
    0.286962360, // %A cal5n=0.130120505 cal5v=0.757099986V cal5n=0.286962360 cal5v=1.707499981V
    //
# elif        (BOARD_SERIAL_NUMBER) == 4
    #warning "(BOARD_SERIAL_NUMBER) == 4"
    0.925441444, // %A cal0n=0.003910068 cal0v=0.000120000V cal0n=0.925441444 cal0v=1.107100010V
    0.925441444, // %A cal1n=0.635619044 cal1v=0.759999990V cal1n=0.925441444 cal1v=1.107100010V
    0.820740156, // %A cal2n=0.083768636 cal2v=0.100000001V cal2n=0.820740156 cal2v=0.980000019V
    0.820740156, // %A cal3n=0.083768636 cal3v=0.100000001V cal3n=0.820740156 cal3v=0.980000019V
    0.279630989, // %A cal4n=0.003910068 cal4v=0.000120000V cal4n=0.279630989 cal4v=1.651999950V
    0.279630989, // %A cal5n=0.129257541 cal5v=0.759999990V cal5n=0.279630989 cal5v=1.651999950V
    //
# elif        (BOARD_SERIAL_NUMBER) == 5
    #warning "(BOARD_SERIAL_NUMBER) == 5"
    // TODO: calibrate BOARD_SERIAL_NUMBER 5 channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE)
    0.75, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.75, // %A cal1n=0.250000000 cal1v=0.300000000V cal1n=0.750000000 cal1v=0.900000000V
    0.75, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.75, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    0.75, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    0.75, // %A cal5n=0.250000000 cal5v=1.500000000V cal5n=0.750000000 cal5v=4.500000000V
    //
# elif        (BOARD_SERIAL_NUMBER) == 6
    #warning "(BOARD_SERIAL_NUMBER) == 6"
    // TODO: calibrate BOARD_SERIAL_NUMBER 6 channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE)
    0.75, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.75, // %A cal1n=0.250000000 cal1v=0.300000000V cal1n=0.750000000 cal1v=0.900000000V
    0.75, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.75, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    0.75, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    0.75, // %A cal5n=0.250000000 cal5v=1.500000000V cal5n=0.750000000 cal5v=4.500000000V
    //
# else
    #warning "BOARD_SERIAL_NUMBER defined but not recognized"
# endif
#else // BOARD_SERIAL_NUMBER data unique to certain boards based on serial number
    #warning "BOARD_SERIAL_NUMBER not defined; using default values"
    0.75, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.75, // %A cal1n=0.250000000 cal1v=0.300000000V cal1n=0.750000000 cal1v=0.900000000V
    0.75, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.75, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    0.75, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    0.75, // %A cal5n=0.250000000 cal5v=1.500000000V cal5n=0.750000000 cal5v=4.500000000V
    //
#endif // BOARD_SERIAL_NUMBER data unique to certain boards based on serial number
};
double calibration_95_V [NUM_PLATFORM_ANALOG_IN_CHANNELS] = {
#ifdef BOARD_SERIAL_NUMBER
// data unique to certain boards based on serial number
# if          (BOARD_SERIAL_NUMBER) == 0
    #warning "(BOARD_SERIAL_NUMBER) == 0"
    // TODO: calibrate BOARD_SERIAL_NUMBER 0 channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE)
    0.9, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.9, // %A cal1n=0.250000000 cal1v=0.300000000V cal1n=0.750000000 cal1v=0.900000000V
    0.9, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.9, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    4.5, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    4.5, // %A cal5n=0.250000000 cal5v=1.500000000V cal5n=0.750000000 cal5v=4.500000000V
    //
# elif        (BOARD_SERIAL_NUMBER) == 1
    #warning "(BOARD_SERIAL_NUMBER) == 1"
    // TODO: calibrate BOARD_SERIAL_NUMBER 1 channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE)
    0.900000000, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    1.167500019, // %A cal1n=0.624736547 cal1v=0.748000026V cal1n=0.979754724 cal1v=1.167500019V
    0.900000000, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.900000000, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    4.500000000, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    1.578999996, // %A cal5n=0.127252869 cal5v=0.748000026V cal5n=0.267205842 cal5v=1.578999996V
    //
# elif        (BOARD_SERIAL_NUMBER) == 2
    #warning "(BOARD_SERIAL_NUMBER) == 2"
    // TODO: calibrate BOARD_SERIAL_NUMBER 2 channels A1/A5(1V0)?
    1.149270058, // %A cal0n=0.001955034 cal0v=0.000120000V cal0n=0.964797903 cal0v=1.149270058V
    1.149270058, // %A cal1n=0.646371711 cal1v=0.761619985V cal1n=0.964797903 cal1v=1.149270058V
    1.004999995, // %A cal2n=0.121437412 cal2v=0.146500006V cal2n=0.833466958 cal2v=1.004999995V
    1.004999995, // %A cal3n=0.121437412 cal3v=0.146500006V cal3n=0.833466958 cal3v=1.004999995V
    1.735399961, // %A cal4n=0.001955034 cal4v=0.000120000V cal4n=0.291349728 cal4v=1.735399961V
    1.735399961, // %A cal5n=0.130005952 cal5v=0.761650026V cal5n=0.291349728 cal5v=1.735399961V
    //
# elif        (BOARD_SERIAL_NUMBER) == 3
    #warning "(BOARD_SERIAL_NUMBER) == 3"
    // TODO: calibrate BOARD_SERIAL_NUMBER 3 channels A2(WE), A3(CE) not quite right yet
    1.149999976, // %A cal0n=0.002932551 cal0v=0.000130000V cal0n=0.966447462 cal0v=1.149999976V
    1.149999976, // %A cal1n=0.643798094 cal1v=0.756900012V cal1n=0.966447462 cal1v=1.149999976V
    0.936999977, // %A cal2n=0.151286051 cal2v=0.181050003V cal2n=0.786534725 cal2v=0.936999977V
    0.936999977, // %A cal3n=0.151286051 cal3v=0.181050003V cal3n=0.786023080 cal3v=0.936999977V
    1.707499981, // %A cal4n=0.002932551 cal4v=0.000130000V cal4n=0.286962360 cal4v=1.707499981V
    1.707499981, // %A cal5n=0.130120505 cal5v=0.757099986V cal5n=0.286962360 cal5v=1.707499981V
    //
# elif        (BOARD_SERIAL_NUMBER) == 4
    #warning "(BOARD_SERIAL_NUMBER) == 4"
    1.107100010, // %A cal0n=0.003910068 cal0v=0.000120000V cal0n=0.925441444 cal0v=1.107100010V
    1.107100010, // %A cal1n=0.635619044 cal1v=0.759999990V cal1n=0.925441444 cal1v=1.107100010V
    0.980000019, // %A cal2n=0.083768636 cal2v=0.100000001V cal2n=0.820740156 cal2v=0.980000019V
    0.980000019, // %A cal3n=0.083768636 cal3v=0.100000001V cal3n=0.820740156 cal3v=0.980000019V
    1.651999950, // %A cal4n=0.003910068 cal4v=0.000120000V cal4n=0.279630989 cal4v=1.651999950V
    1.651999950, // %A cal5n=0.129257541 cal5v=0.759999990V cal5n=0.279630989 cal5v=1.651999950V
    //
# elif        (BOARD_SERIAL_NUMBER) == 5
    #warning "(BOARD_SERIAL_NUMBER) == 5"
    // TODO: calibrate BOARD_SERIAL_NUMBER 5 channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE)
    0.9, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.9, // %A cal1n=0.250000000 cal1v=0.300000000V cal1n=0.750000000 cal1v=0.900000000V
    0.9, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.9, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    4.5, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    4.5, // %A cal5n=0.250000000 cal5v=1.500000000V cal5n=0.750000000 cal5v=4.500000000V
    //
# elif        (BOARD_SERIAL_NUMBER) == 6
    #warning "(BOARD_SERIAL_NUMBER) == 6"
    // TODO: calibrate BOARD_SERIAL_NUMBER 6 channels A0/A4(CSA*100/3.34=mA), A1/A5(1V0), A2(WE), A3(CE)
    0.9, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.9, // %A cal1n=0.250000000 cal1v=0.300000000V cal1n=0.750000000 cal1v=0.900000000V
    0.9, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.9, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    4.5, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    4.5, // %A cal5n=0.250000000 cal5v=1.500000000V cal5n=0.750000000 cal5v=4.500000000V
    //
# else
    #warning "BOARD_SERIAL_NUMBER defined but not recognized"
# endif
#else // BOARD_SERIAL_NUMBER data unique to certain boards based on serial number
    #warning "BOARD_SERIAL_NUMBER not defined; using default values"
    0.9, // %A cal0n=0.250000000 cal0v=0.300000000V cal0n=0.750000000 cal0v=0.900000000V
    0.9, // %A cal1n=0.250000000 cal1v=0.300000000V cal1n=0.750000000 cal1v=0.900000000V
    0.9, // %A cal2n=0.250000000 cal2v=0.300000000V cal2n=0.750000000 cal2v=0.900000000V
    0.9, // %A cal3n=0.250000000 cal3v=0.300000000V cal3n=0.750000000 cal3v=0.900000000V
    4.5, // %A cal4n=0.250000000 cal4v=1.500000000V cal4n=0.750000000 cal4v=4.500000000V
    4.5, // %A cal5n=0.250000000 cal5v=1.500000000V cal5n=0.750000000 cal5v=4.500000000V
    //
#endif // BOARD_SERIAL_NUMBER data unique to certain boards based on serial number
};
#endif // HAS_Platform_AIN_Calibration
#if HAS_Platform_AIN_Calibration
double CalibratedNormValue(int channel_0_5, double raw_normValue_0_1)
{
    // TODO: return corrected normValue_0_1 using two-point linear calibration
    // point 1: calibration_05_normValue_0_1[ch], calibration_05_V[ch]
    // point 2: calibration_95_normValue_0_1[ch], calibration_95_V[ch]
    // validate that there is enough span to get sensible results
    //
    int ch = channel_0_5;
    // cmdLine.serial().printf(" %%A cal%dn=%1.9f cal%dv=%1.9fV cal%dn=%1.9f cal%dv=%1.9fV\r\n",
    //     ch, calibration_05_normValue_0_1[ch],
    //     ch, calibration_05_V[ch],
    //     ch, calibration_95_normValue_0_1[ch],
    //     ch, calibration_95_V[ch]
    // );
    // cmdLine.serial().printf("\r\n adc_full_scale_voltage[%d] = %1.6fV",
    //     ch,
    //     adc_full_scale_voltage[ch]
    // );
    // raw normValue nominal 5% and 95% points
    double raw_05_normValue = calibration_05_normValue_0_1[ch];
    double raw_95_normValue = calibration_95_normValue_0_1[ch];
    // calibrated normValue nominal 5% and 95% points
    // divide V/adc_full_scale_voltage[0] to get normValue_0_1
    double cal_05_normValue = calibration_05_V[ch] / adc_full_scale_voltage[ch];
    double cal_95_normValue = calibration_95_V[ch] / adc_full_scale_voltage[ch];
    // cmdLine.serial().printf("\r\n CalibratedNormValue(%d, %1.6f) cal_05_normValue = %1.6f",
    //     ch,
    //     raw_normValue_0_1,
    //     cal_05_normValue
    // );
    // cmdLine.serial().printf("\r\n CalibratedNormValue(%d, %1.6f) cal_95_normValue = %1.6f",
    //     ch,
    //     raw_normValue_0_1,
    //     cal_95_normValue
    // );
    //
    double span_raw = raw_95_normValue - raw_05_normValue;
    double span_cal = cal_95_normValue - cal_05_normValue;
    // cmdLine.serial().printf("\r\n CalibratedNormValue(%d, %1.6f) span_raw = %1.6f",
    //     ch,
    //     raw_normValue_0_1,
    //     span_raw
    // );
    // cmdLine.serial().printf("\r\n CalibratedNormValue(%d, %1.6f) span_cal = %1.6f",
    //     ch,
    //     raw_normValue_0_1,
    //     span_cal
    // );
    // if calibration is not valid, return unmodified normValue_0_1 and print a warning
    if (span_raw < 0.001) {
        cmdLine.serial().printf("\r\n! CalibratedNormValue(%d, %1.6f) ERRRRRR span_raw = %1.6f",
            ch,
            raw_normValue_0_1,
            span_raw
        );
        return raw_normValue_0_1;
    }
    if (span_cal < 0.001) {
        cmdLine.serial().printf("\r\n! CalibratedNormValue(%d, %1.6f) ERRRRRR span_cal = %1.6f",
            ch,
            raw_normValue_0_1,
            span_cal
        );
        return raw_normValue_0_1;
    }
    double slope_correction = span_cal / span_raw;
    double corrected_normValue_0_1 = cal_05_normValue + ((raw_normValue_0_1 - raw_05_normValue) * slope_correction);
    // cmdLine.serial().printf("\r\n CalibratedNormValue(%d, %1.6f) slope_correction = %1.6f",
    //     ch,
    //     raw_normValue_0_1,
    //     slope_correction
    // );
    // cmdLine.serial().printf("\r\n CalibratedNormValue(%d, %1.6f) corrected_normValue_0_1 = %1.6f\r\n",
    //     ch,
    //     raw_normValue_0_1,
    //     corrected_normValue_0_1
    // );
    return corrected_normValue_0_1;
}
#endif // HAS_Platform_AIN_Calibration
// Option to customize channel names in datalog header line
#ifndef HAS_Platform_AIN_customChannelHeader
#define HAS_Platform_AIN_customChannelHeader 1
#endif
#if HAS_Platform_AIN_customChannelHeader // Optional custom per-channel header suffix
const char* const Platform_AIN_customChannelHeader_ch[NUM_PLATFORM_ANALOG_IN_CHANNELS] = {
    "=AIN4", // MAX40108: AIN0_1V0_current_   0.591202*100/3.34 = 17.70065868263473mA
    "=AIN5", // MAX40108: AIN1_1V0_voltage
    "WE", // MAX40108: AIN2_WE
    "CE", // MAX40108: AIN3_CE
    "*100/3.34=mA", // MAX40108: AIN4_*100/3.34=mA
    "CELL_VOLTAGE", // MAX40108: AIN5_CELL_VOLTAGE
};
#endif // HAS_Platform_AIN_customChannelHeader
//--------------------------------------------------
// Option to log platform analog inputs as raw LSB code
#ifndef LOG_PLATFORM_ANALOG_IN_LSB
#define LOG_PLATFORM_ANALOG_IN_LSB 0
#endif
#if LOG_PLATFORM_ANALOG_IN_LSB
int Platform_LSB[NUM_PLATFORM_ANALOG_IN_CHANNELS];
#endif

//--------------------------------------------------
// Option to use platform analog inputs as Voltage
#ifndef LOG_PLATFORM_ANALOG_IN_VOLTS
#define LOG_PLATFORM_ANALOG_IN_VOLTS 1
#endif
#if LOG_PLATFORM_ANALOG_IN_VOLTS
double Platform_Voltage[NUM_PLATFORM_ANALOG_IN_CHANNELS];
#endif
#endif // defined(LOG_PLATFORM_AIN)

//--------------------------------------------------
// Option to use Command forwarding to Auxiliary serial port
// Command forwarding to Auxiliary serial port TX/RX
// Command forwarding to AUX serial TX/RX cmdLine_AUXserial
// Command forwarding to DAPLINK serial TX/RX cmdLine_DAPLINKserial
// prefer cmdLine_AUXserial if available, else cmdLine_DAPLINKserial; else we don't have this feature
#ifndef USE_AUX_SERIAL_CMD_FORWARDING
#define USE_AUX_SERIAL_CMD_FORWARDING 1
#endif // USE_AUX_SERIAL_CMD_FORWARDING
#if USE_AUX_SERIAL_CMD_FORWARDING
// Command forwarding to Auxiliary serial port TX/RX #257 -- global parameters
const size_t RX_STRING_BUF_SIZE = 1000;
int g_auxSerialCom_baud = 9600; //!< baud rate Auxiliary serial port
// transmit command string by AUX TX
#if 0
int g_auxSerialCom_tx_wait_echo = 0;       //!< TX wait for each character echo?
int g_auxSerialCom_tx_char_delay_ms = 0;   //!< TX delay after each char?
int g_auxSerialCom_tx_line_delay_ms = 0;   //!< TX delay after each CR/LF?
#endif
//  capture received string from AUX RX
Timer g_auxSerialCom_Timer;
int g_auxSerialCom_Timer_begin_message_ms = 0; //!< start of message
int g_auxSerialCom_Timer_begin_rx_idle_ms = 0; //!< recent RX character timestamp
int g_auxSerialCom_message_ms = 10000;     //!< maximum RX message total response time
int g_auxSerialCom_rx_idle_ms = 500;     //!< maximum RX message idle time between characters
int g_auxSerialCom_rx_max_count = RX_STRING_BUF_SIZE-1;   //!< maximum RX message total length
const int aux_serial_cmd_forwarding_rx_eot_not_used = 'x';
int g_auxSerialCom_rx_eot = aux_serial_cmd_forwarding_rx_eot_not_used;         //!< capture RX until match end of text char
//~ int g_auxSerialCom_rx_eot = 0;     //!< capture RX until match end of text string?
#endif // USE_AUX_SERIAL_CMD_FORWARDING
#if USE_AUX_SERIAL_CMD_FORWARDING
    // TODO WIP Command forwarding to Auxiliary serial port TX/RX #257
    // prefer cmdLine_AUXserial if available, else cmdLine_DAPLINKserial; else we don't have this feature
# if HAS_AUX_SERIAL
    // TODO WIP Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257
# elif HAS_DAPLINK_SERIAL
    // TODO WIP Command forwarding to DAPLINK serial TX/RX cmdLine_DAPLINKserial #257
# else // neither HAS_AUX_SERIAL HAS_DAPLINK_SERIAL
#warning "USE_AUX_SERIAL_CMD_FORWARDING should not be enabled without HAS_AUX_SERIAL or HAS_DAPLINK_SERIAL"
# endif // HAS_AUX_SERIAL HAS_DAPLINK_SERIAL
# if HAS_AUX_SERIAL
    // TODO WIP Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257
# endif // HAS_AUX_SERIAL
# if HAS_DAPLINK_SERIAL
    // TODO WIP Command forwarding to DAPLINK serial TX/RX cmdLine_DAPLINKserial #257
# endif // HAS_DAPLINK_SERIAL
#endif // USE_AUX_SERIAL_CMD_FORWARDING

// CODE GENERATOR: example code for ADC: serial port declaration
//--------------------------------------------------
// Declare the Serial driver
// default baud rate settings are 9600 8N1
// install device driver from http://developer.mbed.org/media/downloads/drivers/mbedWinSerial_16466.exe
// see docs https://docs.mbed.com/docs/mbed-os-handbook/en/5.5/getting_started/what_need/
//--------------------------------------------------
#if defined(TARGET_MAX32630)
//Serial UART0serial(UART0_TX,UART0_RX); // tx,rx UART0 MAX32630FTHR: P0_1,P0_0 (Bluetooth PAN1326B)
//Serial UART1serial(UART1_TX,UART1_RX); // tx,rx UART1 MAX32630FTHR: P2_1,P2_0 (DAPLINK)
//Serial UART2serial(UART2_TX,UART2_RX); // tx,rx UART2 MAX32630FTHR: P3_1,P3_0 (J1.15,J1.14)
//Serial UART3serial(UART3_TX,UART3_RX); // tx,rx UART3 MAX32630FTHR: P5_4,P5_3 (J3.7,J3.8)
//
// TX/RX auxiliary UART port cmdLine_AUXserial AUXserial
Serial AUXserial(UART2_TX,UART2_RX); // tx,rx UART2 MAX32630FTHR: P3_1,P3_0 (J1.15,J1.14)
//Serial J3AUXserial(UART3_TX,UART3_RX); // tx,rx UART3 MAX32630FTHR: P5_4,P5_3 (J3.7,J3.8)
//Serial BTAUXserial(UART0_TX,UART0_RX); // tx,rx UART0 MAX32630FTHR: P0_1,P0_0 (Bluetooth PAN1326B)
    #define HAS_AUX_SERIAL 1
//
// Hardware serial port over DAPLink
// The default baud rate for the DapLink UART is 9600
Serial DAPLINKserial(P2_1,P2_0); // tx,rx UART1 MAX32630FTHR: P2_1,P2_0 (DAPLINK)
    #define HAS_DAPLINK_SERIAL 1
//
// Virtual serial port over USB
    #include "USBSerial.h"
// The baud rate does not affect the virtual USBSerial UART.
USBSerial serial;
//--------------------------------------------------
#elif defined(TARGET_MAX32625MBED)
//Serial UART0serial(UART0_TX,UART0_RX); // tx,rx UART0 MAX32625MBED: P0_1,P0_0 (Arduino D1,D0)
//Serial UART1serial(UART1_TX,UART1_RX); // tx,rx UART1 MAX32625MBED: P2_1,P2_0 (DAPLINK)
//Serial UART2serial(UART2_TX,UART2_RX); // tx,rx UART2 MAX32625MBED: P3_1,P3_0 (J15-LEDgreen,LEDred)
//
// Hardware serial port over DAPLink
// The default baud rate for the DapLink UART is 9600
Serial DAPLINKserial(P2_1,P2_0); // tx,rx UART1 MAX32625MBED: P2_1,P2_0 (DAPLINK)
    #define HAS_DAPLINK_SERIAL 1
//
// Virtual serial port over USB
    #include "USBSerial.h"
// The baud rate does not affect the virtual USBSerial UART.
USBSerial serial;
//--------------------------------------------------
#elif defined(TARGET_MAX32625PICO)
//Serial UART0serial(UART0_TX,UART0_RX); // tx,rx UART0 MAX32625PICO: P0_1,P0_0 (pin 19/20)
//Serial UART1serial(UART1_TX,UART1_RX); // tx,rx UART1 MAX32625PICO: P2_1,P2_0 (underside?)
//Serial UART2serial(UART2_TX,UART2_RX); // tx,rx UART2 MAX32625PICO: P3_1,P3_0 (DAPLINK)
//
// TX/RX auxiliary UART port cmdLine_AUXserial AUXserial
Serial AUXserial(UART0_TX,UART0_RX); // tx,rx UART0 MAX32625PICO: P0_1,P0_0 (pin 19/20)
    #define HAS_AUX_SERIAL 1
//
// Hardware serial port over DAPLink
Serial DAPLINKserial(UART2_TX,UART2_RX); // tx,rx UART2 MAX32625PICO: P3_1,P3_0 (DAPLINK)
    #define HAS_DAPLINK_SERIAL 1
//
// Virtual serial port over USB
    #include "USBSerial.h"
// The baud rate does not affect the virtual USBSerial UART.
USBSerial serial;
//--------------------------------------------------
#elif defined(TARGET_MAX40108DEMOP2U9)
//Serial UART0serial(UART0_TX,UART0_RX); // tx,rx UART0 MAX40108DEMOP2U9: P0_1,P0_0 (J90.1/J90.0 to console)
//Serial UART1serial(UART1_TX,UART1_RX); // tx,rx UART1 MAX40108DEMOP2U9: P2_1,P2_0 (DAPLINK)
//Serial UART2serial(UART2_TX,UART2_RX); // tx,rx UART2 MAX40108DEMOP2U9: P3_1,P3_0 (unavailable)
//
// TX/RX auxiliary UART port cmdLine_AUXserial AUXserial is used as main serial port in MAX40108 Demo board
Serial serial(UART0_TX,UART0_RX); // tx,rx UART0 MAX40108DEMOP2U9: P0_1,P0_0 (J90.1/J90.0 to console)
//    #define HAS_AUX_SERIAL 1
//
// Hardware serial port over DAPLink
// connection to external MAX32625PICO(DAPLINK) needed TX/RX swap in firmware.
// MAX32625PICO(DAPLINK) drives DAPLINK.8, listens on DAPLINK.6.
// See AN6350 MAX32625 Users Guide 7.5.2.3.1 TX and RX Pin Mapping for UART 1 -- Mapping Option B
#ifdef BOARD_SERIAL_NUMBER
// data unique to certain boards based on serial number
# if          (BOARD_SERIAL_NUMBER) == 0
    #warning "(BOARD_SERIAL_NUMBER) == 0"
// s/n 0 is not really a MAX40108 board, it's actually a MAX32625PICO used for early development. So no swap.
Serial DAPLINKserial(UART1_TX,UART1_RX); // tx,rx UART1 MAX40108DEMOP2U9: P2_1,P2_0 (DAPLINK) Mapping Option A (normal)
//Serial DAPLINKserial(UART1_RX,UART1_TX); // tx,rx UART1 MAX40108DEMOP2U9: P2_1,P2_0 (DAPLINK) Mapping Option B (TX/RX-swap)
    #define HAS_DAPLINK_SERIAL 1
    //
// # elif        (BOARD_SERIAL_NUMBER) == 1
//     #warning "(BOARD_SERIAL_NUMBER) == 1"
//     //
// # elif        (BOARD_SERIAL_NUMBER) == 2
//     #warning "(BOARD_SERIAL_NUMBER) == 2"
//     //
// # elif        (BOARD_SERIAL_NUMBER) == 3
//     #warning "(BOARD_SERIAL_NUMBER) == 3"
//     //
// # elif        (BOARD_SERIAL_NUMBER) == 4
//     #warning "(BOARD_SERIAL_NUMBER) == 4"
//     //
// # elif        (BOARD_SERIAL_NUMBER) == 5
//     #warning "(BOARD_SERIAL_NUMBER) == 5"
//     //
// # elif        (BOARD_SERIAL_NUMBER) == 6
//     #warning "(BOARD_SERIAL_NUMBER) == 6"
//     //
# else
    // #warning "BOARD_SERIAL_NUMBER defined but not recognized"
//Serial DAPLINKserial(UART1_TX,UART1_RX); // tx,rx UART1 MAX40108DEMOP2U9: P2_1,P2_0 (DAPLINK) Mapping Option A (normal)
Serial DAPLINKserial(UART1_RX,UART1_TX); // tx,rx UART1 MAX40108DEMOP2U9: P2_1,P2_0 (DAPLINK) Mapping Option B (TX/RX-swap)
    #define HAS_DAPLINK_SERIAL 1
# endif
#else // BOARD_SERIAL_NUMBER data unique to certain boards based on serial number
    #warning "BOARD_SERIAL_NUMBER not defined; using default values"
//Serial DAPLINKserial(UART1_TX,UART1_RX); // tx,rx UART1 MAX40108DEMOP2U9: P2_1,P2_0 (DAPLINK) Mapping Option A (normal)
Serial DAPLINKserial(UART1_RX,UART1_TX); // tx,rx UART1 MAX40108DEMOP2U9: P2_1,P2_0 (DAPLINK) Mapping Option B (TX/RX-swap)
    #define HAS_DAPLINK_SERIAL 1
//
#endif // BOARD_SERIAL_NUMBER data unique to certain boards based on serial number
//
// Serial AUXserial(UART1_TX,UART1_RX); // tx,rx UART2 MAX40108DEMOP2U9: P3_1,P3_0 (unavailable)
//     #define HAS_AUX_SERIAL 1
//
// Virtual serial port over USB
//    #include "USBSerial.h"
// The baud rate does not affect the virtual USBSerial UART.
//USBSerial serial;
//--------------------------------------------------
#elif defined(TARGET_MAX32620FTHR)
#warning "TARGET_MAX32620FTHR not previously tested; need to define serial pins..."
//Serial UART0serial(UART0_TX,UART0_RX); // tx,rx UART0 MAX32620FTHR: P0_1,P0_0 (PMOD0.2,PMOD0.3)
//Serial UART1serial(UART1_TX,UART1_RX); // tx,rx UART1 MAX32620FTHR: P2_1,P2_0 (DAPLINK)
//Serial UART2serial(UART2_TX,UART2_RX); // tx,rx UART2 MAX32620FTHR: P3_1,P3_0 (J1.15,J1.14)
//Serial UART3serial(UART3_TX,UART3_RX); // tx,rx UART3 MAX32620FTHR: P5_4,P5_3 (J2.7,J2.8)
//
// TX/RX auxiliary UART port cmdLine_AUXserial AUXserial
Serial serial(UART2_TX,UART2_RX); // tx,rx UART2 MAX32620FTHR: P3_1,P3_0 (J1.15,J1.14)
//Serial AUXserial(UART2_TX,UART2_RX); // tx,rx UART2 MAX32620FTHR: P3_1,P3_0 (J1.15,J1.14)
Serial AUXserial(UART3_TX,UART3_RX); // tx,rx UART3 MAX32620FTHR: P5_4,P5_3 (J2.7,J2.8)
//Serial PMOD0AUXserial(UART0_TX,UART0_RX); // tx,rx UART0 MAX32620FTHR: P0_1,P0_0 (PMOD0.2,PMOD0.3)
    #define HAS_AUX_SERIAL 1
//
// Hardware serial port over DAPLink
// The default baud rate for the DapLink UART is 9600
Serial DAPLINKserial(USBTX, USBRX);     // tx,rx MAX32620FTHR: P2_1,P2_0
//Serial DAPLINKserial(STDIO_UART_TX, STDIO_UART_RX);     // tx, rx
    #define HAS_DAPLINK_SERIAL 1
//
// Virtual serial port over USB
//    #include "USBSerial.h"
// The baud rate does not affect the virtual USBSerial UART.
//USBSerial serial; // MAX32620FTHR: USBSerial crash??
#warning "TARGET_MAX32620FTHR not previously tested; USBSerial crash?"
//--------------------------------------------------
#elif defined(TARGET_MAX32600)
//Serial UART0serial(UART0_TX,UART0_RX); // tx,rx UART0 MAX32600MBED: P1_1,P1_0 (Arduino D1,D0)(DAPLINK)
//Serial UART1serial(UART1_TX,UART1_RX); // tx,rx UART2 MAX32625MBED: P1_3,P1_2 (Arduino D3,D2)
//Serial UART2serial(UART2_TX,UART2_RX); // tx,rx UART1 MAX32625MBED: P7_3,P7_2 ( ?? )
//
// Hardware serial port over DAPLink
// The default baud rate for the DapLink UART is 9600
Serial DAPLINKserial(P1_1,P1_0); // tx,rx UART0 MAX32600MBED: P1_1,P1_0 (Arduino D1,D0)(DAPLINK)
    #define HAS_DAPLINK_SERIAL 1
//
// Virtual serial port over USB
    #include "USBSerial.h"
// The baud rate does not affect the virtual USBSerial UART.
USBSerial serial;
//--------------------------------------------------
#elif defined(TARGET_NUCLEO_F446RE) || defined(TARGET_NUCLEO_F401RE)
Serial serial(SERIAL_TX, SERIAL_RX);     // tx, rx
//--------------------------------------------------
#else
#if defined(SERIAL_TX)
#warning "target not previously tested; guess serial pins are SERIAL_TX, SERIAL_RX..."
Serial serial(SERIAL_TX, SERIAL_RX);     // tx, rx
#elif defined(USBTX)
#warning "target not previously tested; guess serial pins are USBTX, USBRX..."
Serial serial(USBTX, USBRX); // tx, rx
#elif defined(UART_TX)
#warning "target not previously tested; guess serial pins are UART_TX, UART_RX..."
Serial serial(UART_TX, UART_RX);     // tx, rx
#else
#warning "target not previously tested; need to define serial pins..."
#endif
#endif
//
#include "CmdLine.h"

# if HAS_AUX_SERIAL
// TX/RX auxiliary UART port cmdLine_AUXserial AUXserial
CmdLine cmdLine_AUXserial(AUXserial, "AUXserial");
uint8_t Datalogger_enable_AUXserial = {
#if USE_AUX_SERIAL_CMD_FORWARDING
    // Command forwarding to Auxiliary serial port;
    // don't accept commands from Auxiliary serial port
    false
#else // USE_AUX_SERIAL_CMD_FORWARDING
    true
#endif // USE_AUX_SERIAL_CMD_FORWARDING
};
# endif // HAS_AUX_SERIAL
# if HAS_DAPLINK_SERIAL
CmdLine cmdLine_DAPLINKserial(DAPLINKserial, "DAPLINK");
uint8_t Datalogger_enable_DAPLINKserial = true;
# endif // HAS_DAPLINK_SERIAL
CmdLine cmdLine(serial, "serial");
uint8_t Datalogger_enable_serial = true;
// CODE GENERATOR: example code for ADC: serial port declaration (end)


//--------------------------------------------------
// command_table: list of commands to perform on button press
#ifndef USE_DATALOGGER_CommandTable
#define USE_DATALOGGER_CommandTable 1
//~ #undef USE_DATALOGGER_CommandTable
#endif
#if USE_DATALOGGER_CommandTable // Run_command_table(onButton1_command_table)
const int COMMAND_TABLE_ROW_MAX = 10;
const int COMMAND_TABLE_COL_MAX = 40;
#endif // USE_DATALOGGER_CommandTable
//
#if USE_DATALOGGER_CommandTable // Run_command_table(onButton1_command_table)
#if HAS_BUTTON1_DEMO_INTERRUPT
// command_table: list of commands to perform on button press
char onButton1_command_table_00[COMMAND_TABLE_COL_MAX] = "# Button1 event";
char onButton1_command_table_01[COMMAND_TABLE_COL_MAX] = "%L91";
char onButton1_command_table_02[COMMAND_TABLE_COL_MAX] = "%H92";
char onButton1_command_table_03[COMMAND_TABLE_COL_MAX] = "%L93";
char onButton1_command_table_04[COMMAND_TABLE_COL_MAX] = "%H91";
char onButton1_command_table_05[COMMAND_TABLE_COL_MAX] = "%L92";
char onButton1_command_table_06[COMMAND_TABLE_COL_MAX] = "%H93";
char onButton1_command_table_07[COMMAND_TABLE_COL_MAX] = "LR";
char onButton1_command_table_08[COMMAND_TABLE_COL_MAX] = "";
char onButton1_command_table_09[COMMAND_TABLE_COL_MAX] = "";
static char* onButton1_command_table[COMMAND_TABLE_ROW_MAX] = {
    onButton1_command_table_00,
    onButton1_command_table_01,
    onButton1_command_table_02,
    onButton1_command_table_03,
    onButton1_command_table_04,
    onButton1_command_table_05,
    onButton1_command_table_06,
    onButton1_command_table_07,
    onButton1_command_table_08,
    onButton1_command_table_09,
};
#endif // HAS_BUTTON1_DEMO_INTERRUPT
#if HAS_BUTTON2_DEMO_INTERRUPT
// command_table: list of commands to perform on button press
char onButton2_command_table_00[COMMAND_TABLE_COL_MAX] = "# Button2 event";
char onButton2_command_table_01[COMMAND_TABLE_COL_MAX] = "%H91";
char onButton2_command_table_02[COMMAND_TABLE_COL_MAX] = "%H92";
char onButton2_command_table_03[COMMAND_TABLE_COL_MAX] = "%L93";
char onButton2_command_table_04[COMMAND_TABLE_COL_MAX] = "";
char onButton2_command_table_05[COMMAND_TABLE_COL_MAX] = "";
char onButton2_command_table_06[COMMAND_TABLE_COL_MAX] = "";
char onButton2_command_table_07[COMMAND_TABLE_COL_MAX] = "";
char onButton2_command_table_08[COMMAND_TABLE_COL_MAX] = "";
char onButton2_command_table_09[COMMAND_TABLE_COL_MAX] = "";
static char* onButton2_command_table[COMMAND_TABLE_ROW_MAX] = {
    onButton2_command_table_00,
    onButton2_command_table_01,
    onButton2_command_table_02,
    onButton2_command_table_03,
    onButton2_command_table_04,
    onButton2_command_table_05,
    onButton2_command_table_06,
    onButton2_command_table_07,
    onButton2_command_table_08,
    onButton2_command_table_09,
};
#endif // HAS_BUTTON2_DEMO_INTERRUPT
#if HAS_BUTTON3_DEMO_INTERRUPT
// command_table: list of commands to perform on button press
char onButton3_command_table_00[COMMAND_TABLE_COL_MAX] = "# Button3 event";
char onButton3_command_table_01[COMMAND_TABLE_COL_MAX] = "%L91";
char onButton3_command_table_02[COMMAND_TABLE_COL_MAX] = "%L92";
char onButton3_command_table_03[COMMAND_TABLE_COL_MAX] = "%H93";
char onButton3_command_table_04[COMMAND_TABLE_COL_MAX] = "";
char onButton3_command_table_05[COMMAND_TABLE_COL_MAX] = "";
char onButton3_command_table_06[COMMAND_TABLE_COL_MAX] = "";
char onButton3_command_table_07[COMMAND_TABLE_COL_MAX] = "";
char onButton3_command_table_08[COMMAND_TABLE_COL_MAX] = "";
char onButton3_command_table_09[COMMAND_TABLE_COL_MAX] = "";
static char* onButton3_command_table[COMMAND_TABLE_ROW_MAX] = {
    onButton3_command_table_00,
    onButton3_command_table_01,
    onButton3_command_table_02,
    onButton3_command_table_03,
    onButton3_command_table_04,
    onButton3_command_table_05,
    onButton3_command_table_06,
    onButton3_command_table_07,
    onButton3_command_table_08,
    onButton3_command_table_09,
};
#endif // HAS_BUTTON3_DEMO_INTERRUPT
#if HAS_BUTTON4_DEMO_INTERRUPT
// command_table: list of commands to perform on button press
char onButton4_command_table_00[COMMAND_TABLE_COL_MAX] = "# Button4 event";
char onButton4_command_table_01[COMMAND_TABLE_COL_MAX] = "# WE>0.7:active";
char onButton4_command_table_02[COMMAND_TABLE_COL_MAX] = "";
char onButton4_command_table_03[COMMAND_TABLE_COL_MAX] = "";
char onButton4_command_table_04[COMMAND_TABLE_COL_MAX] = "";
char onButton4_command_table_05[COMMAND_TABLE_COL_MAX] = "";
char onButton4_command_table_06[COMMAND_TABLE_COL_MAX] = "";
char onButton4_command_table_07[COMMAND_TABLE_COL_MAX] = "";
char onButton4_command_table_08[COMMAND_TABLE_COL_MAX] = "";
char onButton4_command_table_09[COMMAND_TABLE_COL_MAX] = "";
static char* onButton4_command_table[COMMAND_TABLE_ROW_MAX] = {
    onButton4_command_table_00,
    onButton4_command_table_01,
    onButton4_command_table_02,
    onButton4_command_table_03,
    onButton4_command_table_04,
    onButton4_command_table_05,
    onButton4_command_table_06,
    onButton4_command_table_07,
    onButton4_command_table_08,
    onButton4_command_table_09,
};
#endif // HAS_BUTTON4_DEMO_INTERRUPT
#if HAS_BUTTON5_DEMO_INTERRUPT
// command_table: list of commands to perform on button press
char onButton5_command_table_00[COMMAND_TABLE_COL_MAX] = "# Button5 event";
char onButton5_command_table_01[COMMAND_TABLE_COL_MAX] = "# WE<0.6:sleep";
char onButton5_command_table_02[COMMAND_TABLE_COL_MAX] = "";
char onButton5_command_table_03[COMMAND_TABLE_COL_MAX] = "";
char onButton5_command_table_04[COMMAND_TABLE_COL_MAX] = "";
char onButton5_command_table_05[COMMAND_TABLE_COL_MAX] = "";
char onButton5_command_table_06[COMMAND_TABLE_COL_MAX] = "";
char onButton5_command_table_07[COMMAND_TABLE_COL_MAX] = "";
char onButton5_command_table_08[COMMAND_TABLE_COL_MAX] = "";
char onButton5_command_table_09[COMMAND_TABLE_COL_MAX] = "";
static char* onButton5_command_table[COMMAND_TABLE_ROW_MAX] = {
    onButton5_command_table_00,
    onButton5_command_table_01,
    onButton5_command_table_02,
    onButton5_command_table_03,
    onButton5_command_table_04,
    onButton5_command_table_05,
    onButton5_command_table_06,
    onButton5_command_table_07,
    onButton5_command_table_08,
    onButton5_command_table_09,
};
#endif // HAS_BUTTON5_DEMO_INTERRUPT
#if HAS_BUTTON6_DEMO_INTERRUPT
// command_table: list of commands to perform on button press
char onButton6_command_table_00[COMMAND_TABLE_COL_MAX] = "# Button6 event";
char onButton6_command_table_01[COMMAND_TABLE_COL_MAX] = "";
char onButton6_command_table_02[COMMAND_TABLE_COL_MAX] = "";
char onButton6_command_table_03[COMMAND_TABLE_COL_MAX] = "";
char onButton6_command_table_04[COMMAND_TABLE_COL_MAX] = "";
char onButton6_command_table_05[COMMAND_TABLE_COL_MAX] = "";
char onButton6_command_table_06[COMMAND_TABLE_COL_MAX] = "";
char onButton6_command_table_07[COMMAND_TABLE_COL_MAX] = "";
char onButton6_command_table_08[COMMAND_TABLE_COL_MAX] = "";
char onButton6_command_table_09[COMMAND_TABLE_COL_MAX] = "";
static char* onButton6_command_table[COMMAND_TABLE_ROW_MAX] = {
    onButton6_command_table_00,
    onButton6_command_table_01,
    onButton6_command_table_02,
    onButton6_command_table_03,
    onButton6_command_table_04,
    onButton6_command_table_05,
    onButton6_command_table_06,
    onButton6_command_table_07,
    onButton6_command_table_08,
    onButton6_command_table_09,
};
#endif // HAS_BUTTON6_DEMO_INTERRUPT
#if HAS_BUTTON7_DEMO_INTERRUPT
// command_table: list of commands to perform on button press
char onButton7_command_table_00[COMMAND_TABLE_COL_MAX] = "# Button7 event";
char onButton7_command_table_01[COMMAND_TABLE_COL_MAX] = "";
char onButton7_command_table_02[COMMAND_TABLE_COL_MAX] = "";
char onButton7_command_table_03[COMMAND_TABLE_COL_MAX] = "";
char onButton7_command_table_04[COMMAND_TABLE_COL_MAX] = "";
char onButton7_command_table_05[COMMAND_TABLE_COL_MAX] = "";
char onButton7_command_table_06[COMMAND_TABLE_COL_MAX] = "";
char onButton7_command_table_07[COMMAND_TABLE_COL_MAX] = "";
char onButton7_command_table_08[COMMAND_TABLE_COL_MAX] = "";
char onButton7_command_table_09[COMMAND_TABLE_COL_MAX] = "";
static char* onButton7_command_table[COMMAND_TABLE_ROW_MAX] = {
    onButton7_command_table_00,
    onButton7_command_table_01,
    onButton7_command_table_02,
    onButton7_command_table_03,
    onButton7_command_table_04,
    onButton7_command_table_05,
    onButton7_command_table_06,
    onButton7_command_table_07,
    onButton7_command_table_08,
    onButton7_command_table_09,
};
#endif // HAS_BUTTON7_DEMO_INTERRUPT
#if HAS_BUTTON8_DEMO_INTERRUPT
// command_table: list of commands to perform on button press
char onButton8_command_table_00[COMMAND_TABLE_COL_MAX] = "# Button8 event";
char onButton8_command_table_01[COMMAND_TABLE_COL_MAX] = "";
char onButton8_command_table_02[COMMAND_TABLE_COL_MAX] = "";
char onButton8_command_table_03[COMMAND_TABLE_COL_MAX] = "";
char onButton8_command_table_04[COMMAND_TABLE_COL_MAX] = "";
char onButton8_command_table_05[COMMAND_TABLE_COL_MAX] = "";
char onButton8_command_table_06[COMMAND_TABLE_COL_MAX] = "";
char onButton8_command_table_07[COMMAND_TABLE_COL_MAX] = "";
char onButton8_command_table_08[COMMAND_TABLE_COL_MAX] = "";
char onButton8_command_table_09[COMMAND_TABLE_COL_MAX] = "";
static char* onButton8_command_table[COMMAND_TABLE_ROW_MAX] = {
    onButton8_command_table_00,
    onButton8_command_table_01,
    onButton8_command_table_02,
    onButton8_command_table_03,
    onButton8_command_table_04,
    onButton8_command_table_05,
    onButton8_command_table_06,
    onButton8_command_table_07,
    onButton8_command_table_08,
    onButton8_command_table_09,
};
#endif // HAS_BUTTON8_DEMO_INTERRUPT
#if HAS_BUTTON9_DEMO_INTERRUPT
// command_table: list of commands to perform on button press
char onButton9_command_table_00[COMMAND_TABLE_COL_MAX] = "# Button9 event";
char onButton9_command_table_01[COMMAND_TABLE_COL_MAX] = "";
char onButton9_command_table_02[COMMAND_TABLE_COL_MAX] = "";
char onButton9_command_table_03[COMMAND_TABLE_COL_MAX] = "";
char onButton9_command_table_04[COMMAND_TABLE_COL_MAX] = "";
char onButton9_command_table_05[COMMAND_TABLE_COL_MAX] = "";
char onButton9_command_table_06[COMMAND_TABLE_COL_MAX] = "";
char onButton9_command_table_07[COMMAND_TABLE_COL_MAX] = "";
char onButton9_command_table_08[COMMAND_TABLE_COL_MAX] = "";
char onButton9_command_table_09[COMMAND_TABLE_COL_MAX] = "";
static char* onButton9_command_table[COMMAND_TABLE_ROW_MAX] = {
    onButton9_command_table_00,
    onButton9_command_table_01,
    onButton9_command_table_02,
    onButton9_command_table_03,
    onButton9_command_table_04,
    onButton9_command_table_05,
    onButton9_command_table_06,
    onButton9_command_table_07,
    onButton9_command_table_08,
    onButton9_command_table_09,
};
#endif // HAS_BUTTON9_DEMO_INTERRUPT
#endif // USE_DATALOGGER_CommandTable
//--------------------------------------------------
#if USE_DATALOGGER_CommandTable
// TODO: avoid excess console noise when a Button event happens during datalogging -- quiet inside Run_command_table()
bool g_Run_command_table_running = false;
void Run_command_table(char* command_table[])
{
    // command_table: perform list of commands
    // TODO: avoid excess console noise when a Button event happens during datalogging -- quiet inside Run_command_table()
    if (g_Run_command_table_running) {
        // TODO: button event chaining is not supported at this time
    } // if (g_Run_command_table_running)
    // TODO: avoid excess console noise when a Button event happens during datalogging -- quiet inside Run_command_table()
    g_Run_command_table_running = true;
    for (int lineIndex = 0; lineIndex < COMMAND_TABLE_ROW_MAX; lineIndex++) {
        if (command_table[lineIndex] == NULL) { break; }
        if (command_table[lineIndex][0] == '\0') { break; }
        cmdLine.clear();
        cmdLine.quiet = g_Run_command_table_running;
        //~ char* strIndex = command_table[lineIndex];
        for (char* strIndex = command_table[lineIndex]; *strIndex != '\0'; strIndex++)
        {
            cmdLine.append(*strIndex);
        }
        cmdLine.append('\r'); // append \r invokes onEOLcommandParser to handle command
    }
    // TODO: avoid excess console noise when a Button event happens during datalogging -- quiet inside Run_command_table()
    g_Run_command_table_running = false;
    cmdLine.quiet = g_Run_command_table_running;
}
#endif // USE_DATALOGGER_CommandTable
//--------------------------------------------------
// When user presses button BUTTON1, perform actions
#if HAS_BUTTON1_DEMO_INTERRUPT
void onButton1FallingEdge(void)
{
    //~ void SelfTest(CmdLine & cmdLine);
    //~ SelfTest(cmdLine_serial);
    //
#if USE_DATALOGGER_CommandTable // Run_command_table(onButton1_command_table)
    // command_table: list of commands to perform on button press
    // command_table: perform list of commands
    Run_command_table(onButton1_command_table);
#endif // USE_DATALOGGER_CommandTable
}
#endif // HAS_BUTTON1_DEMO_INTERRUPT

//--------------------------------------------------
// When user presses button BUTTON2, perform actions
#if HAS_BUTTON2_DEMO_INTERRUPT
void onButton2FallingEdge(void)
{
    // TBD demo configuration
    // TODO diagnostic LED
    // led1 = LED_OFF; led2 = LED_OFF; led3 = LED_ON;     // diagnostic rbg led BLUE
    //
#if USE_DATALOGGER_CommandTable // Run_command_table(onButton1_command_table)
    // command_table: list of commands to perform on button press
    // command_table: perform list of commands
    Run_command_table(onButton2_command_table);
#endif // USE_DATALOGGER_CommandTable
}
#endif // HAS_BUTTON2_DEMO_INTERRUPT

//--------------------------------------------------
// When user presses button BUTTON3, perform actions
#if HAS_BUTTON3_DEMO_INTERRUPT
void onButton3FallingEdge(void)
{
#if USE_DATALOGGER_CommandTable // Run_command_table(onButton1_command_table)
    // command_table: list of commands to perform on button press
    // command_table: perform list of commands
    Run_command_table(onButton3_command_table);
#endif // USE_DATALOGGER_CommandTable
}
#endif // HAS_BUTTON3_DEMO_INTERRUPT
#if HAS_BUTTON4_DEMO_INTERRUPT
void onButton4FallingEdge(void)
{
#if USE_DATALOGGER_CommandTable
    Run_command_table(onButton4_command_table);
#endif // USE_DATALOGGER_CommandTable
}
#endif // HAS_BUTTON4_DEMO_INTERRUPT
#if HAS_BUTTON5_DEMO_INTERRUPT
void onButton5FallingEdge(void)
{
#if USE_DATALOGGER_CommandTable
    Run_command_table(onButton5_command_table);
#endif // USE_DATALOGGER_CommandTable
}
#endif // HAS_BUTTON5_DEMO_INTERRUPT
#if HAS_BUTTON6_DEMO_INTERRUPT
void onButton6FallingEdge(void)
{
#if USE_DATALOGGER_CommandTable
    Run_command_table(onButton6_command_table);
#endif // USE_DATALOGGER_CommandTable
}
#endif // HAS_BUTTON6_DEMO_INTERRUPT
#if HAS_BUTTON7_DEMO_INTERRUPT
void onButton7FallingEdge(void)
{
#if USE_DATALOGGER_CommandTable
    Run_command_table(onButton7_command_table);
#endif // USE_DATALOGGER_CommandTable
}
#endif // HAS_BUTTON7_DEMO_INTERRUPT
#if HAS_BUTTON8_DEMO_INTERRUPT
void onButton8FallingEdge(void)
{
#if USE_DATALOGGER_CommandTable
    Run_command_table(onButton8_command_table);
#endif // USE_DATALOGGER_CommandTable
}
#endif // HAS_BUTTON8_DEMO_INTERRUPT
#if HAS_BUTTON9_DEMO_INTERRUPT
void onButton9FallingEdge(void)
{
#if USE_DATALOGGER_CommandTable
    Run_command_table(onButton9_command_table);
#endif // USE_DATALOGGER_CommandTable
}
#endif // HAS_BUTTON9_DEMO_INTERRUPT

#if USE_CMDLINE_MENUS // support CmdLine command menus
//--------------------------------------------------
inline void print_command_prompt()
{
    //~ Serial.println(F(">"));
    cmdLine.serial().printf("\r\n> ");
}
#endif // USE_CMDLINE_MENUS support CmdLine command menus

#if USE_CMDLINE_MENUS // support CmdLine command menus
//--------------------------------------------------
void main_menu_status(CmdLine & cmdLine)
{
    cmdLine.serial().printf("\r\nMain menu");
#if APPLICATION_MAX5715 // main_menu_status banner
    cmdLine.serial().printf(" MAX5715 12-bit 4-ch SPI VOUT DAC");
#elif APPLICATION_MAX11131 // main_menu_status banner
    cmdLine.serial().printf(" MAX11131 12-bit 3MSps 16-ch ADC");
#elif APPLICATION_MAX5171 // main_menu_status banner
    cmdLine.serial().printf(" MAX5171 14-bit Force/Sense VOUT DAC");
#elif APPLICATION_MAX11410 // main_menu_status banner
    cmdLine.serial().printf(" MAX11410 24-bit 1.9ksps Delta-Sigma ADC");
#elif APPLICATION_MAX12345 // main_menu_status banner
    cmdLine.serial().printf(" MAX12345");
#elif MAX40108_DEMO // main_menu_status banner
    cmdLine.serial().printf(" MAX40108_U%d", MAX40108_DEMO);
#ifdef BOARD_SERIAL_NUMBER
// data unique to certain boards based on serial number
    cmdLine.serial().printf(" [s/n %d]", BOARD_SERIAL_NUMBER);
#else // BOARD_SERIAL_NUMBER data unique to certain boards based on serial number
    #warning "BOARD_SERIAL_NUMBER not defined; using default values"
//
#endif // BOARD_SERIAL_NUMBER data unique to certain boards based on serial number
#ifdef FW_REV
    cmdLine.serial().printf(" [FW_REV %d]", FW_REV);
#endif // FW_REV
#else
    //cmdLine.serial().printf(" ");
#endif
    //cmdLine.serial().printf(" %s", TARGET_NAME);
    if (cmdLine.nameStr())
    {
        cmdLine.serial().printf(" [%s]", cmdLine.nameStr());
    }
#if HAS_BUTTON1_DEMO_INTERRUPT
    cmdLine.serial().printf(" [Button1");
#if HAS_BUTTON2_DEMO_INTERRUPT
    cmdLine.serial().printf("2");
#endif
#if HAS_BUTTON3_DEMO_INTERRUPT
    cmdLine.serial().printf("3");
#endif
#if HAS_BUTTON4_DEMO_INTERRUPT
    cmdLine.serial().printf("4");
#endif
#if HAS_BUTTON5_DEMO_INTERRUPT
    cmdLine.serial().printf("5");
#endif
#if HAS_BUTTON6_DEMO_INTERRUPT
    cmdLine.serial().printf("6");
#endif
#if HAS_BUTTON7_DEMO_INTERRUPT
    cmdLine.serial().printf("7");
#endif
#if HAS_BUTTON8_DEMO_INTERRUPT
    cmdLine.serial().printf("8");
#endif
#if HAS_BUTTON9_DEMO_INTERRUPT
    cmdLine.serial().printf("9");
#endif
    cmdLine.serial().printf("]");
#endif // HAS_BUTTON1_DEMO_INTERRUPT
#if HAS_BUTTON1_DEMO
    // print BUTTON1 status
    cmdLine.serial().printf("\r\n BUTTON1 = %d", button1.read());
#endif
#if HAS_BUTTON2_DEMO
    // print BUTTON2 status
    cmdLine.serial().printf("\r\n BUTTON2 = %d", button2.read());
#endif
#if HAS_BUTTON3_DEMO
    // print BUTTON3 status
    cmdLine.serial().printf("\r\n BUTTON3 = %d", button3.read());
#endif
    cmdLine.serial().printf("\r\n ? -- help");
}
#endif // USE_CMDLINE_MENUS support CmdLine command menus

//--------------------------------------------------
#ifndef USE_DATALOGGER_TRIGGER_HELP_BRIEF
#define USE_DATALOGGER_TRIGGER_HELP_BRIEF 1
//~ #undef USE_DATALOGGER_TRIGGER_HELP_BRIEF
#endif // USE_DATALOGGER_TRIGGER_HELP_BRIEF

#if USE_CMDLINE_MENUS // support CmdLine command menus
//--------------------------------------------------
void main_menu_help(CmdLine & cmdLine)
{
    // ? -- help
    //~ cmdLine.serial().printf("\r\nMenu:");
    //
    cmdLine.serial().printf("\r\n # -- lines beginning with # are comments");
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
    cmdLine.serial().printf("\r\n ! -- Init");
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
#if USE_SELFTEST
    cmdLine.serial().printf("\r\n . -- SelfTest");
#endif // USE_SELFTEST
#if USE_STAR_REG_READWRITE // * command read/write reg *reg? *reg=value
// CODE GENERATOR: help menu if has_register_write_command: *regname? -- read register; *regname=regvalue -- write register
    cmdLine.serial().printf("\r\n * -- read core registers\r\n *regname? -- read register\r\n *regname=regvalue -- write register");
    // cmdLine.serial().printf("\r\n 01 23 45 67 89 ab cd ef -- write and read raw hex codes");
#endif // USE_STAR_REG_READWRITE
//
#if USE_AUX_SERIAL_CMD_FORWARDING
    // Command forwarding to Auxiliary serial port TX/RX #257 -- main_menu_help
    //~ cmdLine.serial().printf("\r\n > -- auxiliary UART port");
    // prefer cmdLine_AUXserial if available, else cmdLine_DAPLINKserial; else we don't have this feature
# if HAS_AUX_SERIAL
    // Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257
    if (cmdLine_AUXserial.nameStr())
    {
        cmdLine.serial().printf("\r\n > -- auxiliary UART port [%s]", cmdLine_AUXserial.nameStr());
    }
# elif HAS_DAPLINK_SERIAL
    // Command forwarding to DAPLINK serial TX/RX cmdLine_DAPLINKserial #257
    if (cmdLine_DAPLINKserial.nameStr())
    {
        cmdLine.serial().printf("\r\n > -- auxiliary UART port [%s]", cmdLine_DAPLINKserial.nameStr());
    }
# endif // HAS_AUX_SERIAL HAS_DAPLINK_SERIAL
# if HAS_AUX_SERIAL || HAS_DAPLINK_SERIAL
    // WIP Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257
    cmdLine.serial().printf("\r\n >xyzzy -- Forward command/data 'xyzzy' to aux TX/RX");
    cmdLine.serial().printf("\r\n >>xyzzy -- Forward 'xyzzy' to aux TX/RX, no key=value parsing");
    cmdLine.serial().printf("\r\n >>>xyzzy -- Forward '>xyzzy' to aux TX/RX, no key=value parsing");
#if 0
    cmdLine.serial().printf("\r\n >tx_wait_echo=%d -- configure TX wait for each character echo", g_auxSerialCom_tx_wait_echo);
    cmdLine.serial().printf("\r\n >tx_char_delay_ms=%d -- configure TX delay after each char", g_auxSerialCom_tx_char_delay_ms);
    cmdLine.serial().printf("\r\n >tx_line_delay_ms=%d -- configure TX delay after each CR/LF", g_auxSerialCom_tx_line_delay_ms);
#endif
#if 1 // HAS_AUX_SERIAL_HELP_BRIEF
    cmdLine.serial().printf("\r\n >baud=%d message_ms=%d rx_idle_ms=%d rx_max_count=%d rx_eot=%d",
        g_auxSerialCom_baud,
        g_auxSerialCom_message_ms,
        g_auxSerialCom_rx_idle_ms,
        g_auxSerialCom_rx_max_count,
        g_auxSerialCom_rx_eot);
#else // HAS_AUX_SERIAL_HELP_BRIEF
    cmdLine.serial().printf("\r\n >baud=%d -- configure aux TX/RX port", g_auxSerialCom_baud);
    cmdLine.serial().printf("\r\n >message_ms=%d -- maximum RX message total response time", g_auxSerialCom_message_ms);
    cmdLine.serial().printf("\r\n >rx_idle_ms=%d -- maximum RX message idle time between characters", g_auxSerialCom_rx_idle_ms);
    cmdLine.serial().printf("\r\n >rx_max_count=%d -- maximum RX message total length", g_auxSerialCom_rx_max_count);
    cmdLine.serial().printf("\r\n >rx_eot=%d -- capture RX until match end of text char (unless %d)", g_auxSerialCom_rx_eot, aux_serial_cmd_forwarding_rx_eot_not_used);
#endif // HAS_AUX_SERIAL_HELP_BRIEF
# endif // HAS_AUX_SERIAL
#endif // USE_AUX_SERIAL_CMD_FORWARDING
//
#if USE_DATALOGGER_TRIGGER // support Datalog trigger
#if USE_DATALOGGER_TRIGGER_HELP_BRIEF
    // Datalog trigger menu
    // brief toplevel heading for datalog help L
    // L -- detailed help for datalog commands
    cmdLine.serial().printf("\r\n L -- halt the datalogger; show more commands");
    // set Datalogger_Trigger to trigger_Halt or trigger_FreeRun
    // Datalogger_Trigger = trigger_Halt // halt the datalogger; continue accepting commands
    // cmdLine.serial().print(F("\r\n L -- halt the datalogger; continue accepting commands"));
    //
    // Datalogger_Trigger = trigger_FreeRun // free run as fast as possible "always-on mode"
    cmdLine.serial().printf("\r\n LR -- Datalog free run as fast as possible");
    //
#if USE_DATALOGGER_SLEEP // support sleep modes in Datalogger_Trigger
    // TODO: USE_DATALOGGER_SLEEP -- support sleep modes in Datalogger_Trigger
    cmdLine.serial().printf("\r\n LT count=%d base=%dms sleep=%d -- Datalog timer",
        g_timer_interval_count, 
        g_timer_interval_msec,
        (int)g_timer_sleepmode
    ); // trigger_Timer
#else // USE_DATALOGGER_SLEEP
    cmdLine.serial().printf("\r\n LT count=%d base=%dms -- Datalog timer", g_timer_interval_count, g_timer_interval_msec); // trigger_Timer
#endif // USE_DATALOGGER_SLEEP
#if USE_DATALOGGER_ActionTable // Datalogger_RunActionTable() supported actions
    //~ cmdLine.serial().printf("\r\n L@ -- configures Datalogger_action_table; show more commands");
#endif // USE_DATALOGGER_ActionTable Datalogger_RunActionTable() supported actions
#else // USE_DATALOGGER_TRIGGER_HELP_BRIEF -----------------------------------------------
    // TODO Datalog trigger menu
    // set Datalogger_Trigger to trigger_Halt or trigger_FreeRun
    // Datalogger_Trigger = trigger_Halt // halt the datalogger; continue accepting commands
    // cmdLine.serial().print(F("\r\n L -- halt the datalogger; continue accepting commands"));
    //
    // Datalogger_Trigger = trigger_FreeRun // free run as fast as possible "always-on mode"
    cmdLine.serial().printf("\r\n LR -- Datalog free run as fast as possible");
    //
    // Datalogger_Trigger = trigger_Timer // timer (configure interval) "intermittent-sleep-mode"
    //~ cmdLine.serial().printf("\r\n LT -- Datalog timer"); // trigger_Timer
    cmdLine.serial().printf("\r\n LT count=%d base=%dms -- Datalog timer", g_timer_interval_count, g_timer_interval_msec); // trigger_Timer
    //
    // TODO: Datalogger_Trigger = trigger_PlatformDigitalInput // platform digital input (configure digital input pin reference)
    //~ cmdLine.serial().printf("\r\n LI -- Datalog _______"); // trigger_PlatformDigitalInput
    // TODO: cmdLine.serial().printf("\r\n LIH3 -- Datalog when input high D3"); // trigger_PlatformDigitalInput
    // TODO: cmdLine.serial().printf("\r\n LIL6 -- Datalog when input low D6"); // trigger_PlatformDigitalInput
    //
#if USE_DATALOGGER_SPIDeviceRegRead
    // TODO: Datalogger_Trigger = trigger_SPIDeviceRegRead // SPI device register read (configure regaddr, mask value, match value)
    //~ cmdLine.serial().printf("\r\n L$ -- Datalog _______"); // trigger_SPIDeviceRegRead
    cmdLine.serial().printf("\r\n L$ count=10 base=500ms addr=xxx data=xxx mask=xxx -- Datalog when SPI read of address matches mask"); // trigger_SPIDeviceRegRead
#endif // USE_DATALOGGER_SPIDeviceRegRead
    //
#if USE_DATALOGGER_ActionTable // Datalogger_RunActionTable() supported actions
    cmdLine.serial().printf("\r\n L@ -- configures Datalogger_action_table; show more commands");
#endif // USE_DATALOGGER_ActionTable Datalogger_RunActionTable() supported actions
    //
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
    // LS<channel ID><verb>: Configure SPI_AIN channels
    // channel ID: 0,1,2,... or - for all channels
    // verb: D for disable, V for Voltage, L for LSB
    cmdLine.serial().printf("\r\n LS-D -- Datalog SPI ADC channel '-'(all) Disable");
    cmdLine.serial().printf("\r\n LS-V -- Datalog SPI ADC channel '-'(all) Volt");
    cmdLine.serial().printf("\r\n LS-L -- Datalog SPI ADC channel '-'(all) LSB");
    cmdLine.serial().printf("\r\n LS2D -- Datalog SPI ADC channel channel 2 Disable");
    cmdLine.serial().printf("\r\n LS3V -- Datalog SPI ADC channel channel 3 Volt");
    cmdLine.serial().printf("\r\n LS4L -- Datalog SPI ADC channel channel 4 LSB");
    //
    // MAX11410 verb for configuring SPI_AIN_Cfg_v_filter_ch[channel_index]
    // cmdLine.serial().print(F("\r\n LS-CF34 -- Datalog SPI ADC channel channel 5 v_filter 0x34"));
    cmdLine.serial().printf("\r\n LS-CF34 -- Datalog SPI ADC channel '-'(all) v_filter 0x34");
    //
    // MAX11410 verb for configuring SPI_AIN_Cfg_v_ctrl_ch[channel_index]
    // cmdLine.serial().print(F("\r\n LS-CC42 -- Datalog SPI ADC channel '-'(all) v_ctrl 0x42"));
    cmdLine.serial().printf("\r\n LS-CC42 -- Datalog SPI ADC channel '-'(all) v_ctrl 0x42");    
    //
    // MAX11410 verb for configuring SPI_AIN_Cfg_v_ctrl_ch[channel_index]
    // cmdLine.serial().print(F("\r\n LS5___ -- Datalog SPI ADC channel channel 5 v_ctrl"));
    // MAX11410 verb for configuring SPI_AIN_Cfg_v_ctrl_ch[channel_index]
    cmdLine.serial().printf("\r\n LS5CU -- Datalog SPI ADC channel channel 5 v_ctrl Unipolar");
    // ((SPI_AIN_Cfg_v_ctrl_ch[channel_index] & 0x40) == 0) ? "BIPOLAR" : "Unipolar"
    cmdLine.serial().printf("\r\n LS5CB -- Datalog SPI ADC channel channel 5 v_ctrl Bipolar");
    // ((SPI_AIN_Cfg_v_ctrl_ch[channel_index] & 0x40) == 0) ? "BIPOLAR" : "Unipolar"
    //
    // MAX11410 verb for configuring SPI_AIN_Cfg_v_pga_ch[channel_index]
    // cmdLine.serial().print(F("\r\n LS5CP00 -- Datalog SPI ADC channel channel 5 v_pga 0x00"));
    cmdLine.serial().printf("\r\n LS-CP00 -- Datalog SPI ADC channel '-'(all) v_pga 0x00");
    //
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
    //
#if defined(LOG_PLATFORM_AIN) // Datalog Arduino platform analog inputs
    // LA<channel ID><verb>: Configure Platform_AIN channels
    // channel ID: 0,1,2,... or - for all channels
    // verb: D for disable, V for Voltage, L for LSB
    cmdLine.serial().printf("\r\n LA-D -- Datalog Platform-AIN all-channel Disable");
#if LOG_PLATFORM_ANALOG_IN_VOLTS
    cmdLine.serial().printf("\r\n LA-V -- Datalog Platform-AIN all-channel Volt");
#endif // LOG_PLATFORM_ANALOG_IN_VOLTS
#if LOG_PLATFORM_ANALOG_IN_LSB
    cmdLine.serial().printf("\r\n LA-L -- Datalog Platform-AIN all-channel LSB");
#endif // LOG_PLATFORM_ANALOG_IN_LSB
    cmdLine.serial().printf("\r\n LA2D -- Datalog Platform-AIN channel 2 Disable");
#if LOG_PLATFORM_ANALOG_IN_VOLTS
    cmdLine.serial().printf("\r\n LA3V -- Datalog Platform-AIN channel 3 Volt");
#endif // LOG_PLATFORM_ANALOG_IN_VOLTS
#if LOG_PLATFORM_ANALOG_IN_LSB
    cmdLine.serial().printf("\r\n LA4L -- Datalog Platform-AIN channel 4 LSB");
#endif // LOG_PLATFORM_ANALOG_IN_LSB
#endif // defined(LOG_PLATFORM_AIN)
#endif // USE_DATALOGGER_TRIGGER_HELP_BRIEF -----------------------------------------------
#endif // USE_DATALOGGER_TRIGGER support Datalog trigger
#if USE_DATALOGGER_TRIGGER_HELP_BRIEF
#else // USE_DATALOGGER_TRIGGER_HELP_BRIEF -----------------------------------------------
# if HAS_AUX_SERIAL
    cmdLine.serial().printf("\r\n L>A -- Datalogger_enable_AUXserial %s", (Datalogger_enable_AUXserial?"disable":"enable"));
# endif // HAS_AUX_SERIAL
# if HAS_DAPLINK_SERIAL
    cmdLine.serial().printf("\r\n L>D -- Datalogger_enable_DAPLINKserial %s", (Datalogger_enable_DAPLINKserial?"disable":"enable"));
# endif // HAS_DAPLINK_SERIAL
    cmdLine.serial().printf("\r\n L>S -- Datalogger_enable_serial %s", (Datalogger_enable_serial?"disable":"enable"));
#endif // USE_DATALOGGER_TRIGGER_HELP_BRIEF -----------------------------------------------
    //
    //cmdLine.serial().print(F("\r\n ! -- Initial Configuration"));
    //
    // % standardize diagnostic commands
    // %Hpin -- digital output high
    // %Lpin -- digital output low
    // %?pin -- digital input
    // %A %Apin -- analog input
    // %Ppin df=xx -- pwm output
    // %Wpin -- measure high pulsewidth input in usec
    // %wpin -- measure low pulsewidth input in usec
    // %I... -- I2C diagnostics
    // %IP -- I2C probe
    // %IC scl=100khz ADDR=? -- I2C configure
    // %IW ADDR=? cmd=? data,data,data -- write
    // %IR ADDR=? RD=? -- read
    // %I^ cmd=? -- i2c_smbus_read_word_data
    // %S... -- SPI diagnostics
    // %SC sclk=1Mhz -- SPI configure
    // %SW -- write (write and read)
    // %SR -- read (alias for %SW because SPI always write and read)
    // A-Z,a-z,0-9 reserved for application use
    //
//--------------------------------------------------
#if USE_DATALOGGER_CommandTable
// command_table: list of commands to perform on button press
// TODO: %B1 submenu for Button1
// TODO: future: %B2 submenu for Button2
// TODO: future: %B3 submenu for Button3
// TODO: %B1@ view command table, similar to L@ for action table
// TODO: %B1@+ command add new row (if there is room to add) (ignore one space before command)
// TODO: %B1@-nnn delete row number nnn (if list is not empty)
// TODO: %B1@-~~~ clear entire command table
// TODO: %B1@nnn command replace row number nnn (ignore one space before command)
// TODO: %B1@! disable button response but keep command table contents
// TODO: %B1@@ enable button response
// TODO: %B1! trigger onButton1FallingEdge() immediately (for test development)
    cmdLine.serial().printf("\r\n %%B1! trigger Button1 event; %%B1@ -- view/edit command table");
#endif // USE_DATALOGGER_CommandTable
//--------------------------------------------------
#if HAS_digitalInOuts
    // %Hpin -- digital output high
    // %Lpin -- digital output low
    // %?pin -- digital input
    cmdLine.serial().printf("\r\n %%Hn {pin:");
    list_digitalInOutPins(cmdLine.serial());
    cmdLine.serial().printf("} -- High Output");
    cmdLine.serial().printf("\r\n %%Ln {pin:");
    list_digitalInOutPins(cmdLine.serial());
    cmdLine.serial().printf("} -- Low Output");
    cmdLine.serial().printf("\r\n %%?n {pin:");
    list_digitalInOutPins(cmdLine.serial());
    cmdLine.serial().printf("} -- Input");
#endif

#if HAS_analogIns
    // Menu A) analogRead A0..7
    // %A %Apin -- analog input
    // analogRead(pinIndex) // analog input pins A0, A1, A2, A3, A4, A5; float voltage = analogRead(A0) * (5.0 / 1023.0)
    cmdLine.serial().printf("\r\n %%A -- analogRead");
#if USE_Platform_AIN_Average
    // %A avg=%d -- help string display Platform_AIN_Average_N
    cmdLine.serial().printf("; %%A avg=%d -- average", Platform_AIN_Average_N);
#endif // USE_Platform_AIN_Average
#if HAS_Platform_AIN_Calibration
    // %A cal? view/export raw calibration values for all channels
    cmdLine.serial().printf("; %%A cal? -- calibration");
#endif // HAS_Platform_AIN_Calibration
#endif

#if HAS_SPI2_MAX541
    // TODO1: MAX541 max541(spi2_max541, spi2_max541_cs);
    cmdLine.serial().printf("\r\n %%D -- DAC output MAX541 (SPI2)");
#endif

#if HAS_I2C // SUPPORT_I2C
    // TODO: support I2C HAS_I2C // SUPPORT_I2C
    // VERIFY: I2C utility commands SUPPORT_I2C
    // VERIFY: report g_I2C_SCL_Hz = (F_CPU / ((TWBR * 2) + 16)) from last Wire_Sr.setClock(I2C_SCL_Hz);
    // %I... -- I2C diagnostics
    // %IP -- I2C probe
    // %IC scl=100khz ADDR=? -- I2C configure
    // %IW byte byte ... byte RD=? ADDR=0x -- write
    // %IR ADDR=? RD=? -- read
    // %I^ cmd=? -- i2c_smbus_read_word_data
    //g_I2C_SCL_Hz = (F_CPU / ((TWBR * 2) + 16));   // 'F_CPU' 'TWBR' not declared in this scope
    cmdLine.serial().printf("\r\n %%IC ADDR=0x%2.2x=(0x%2.2x>>1) SCL=%d=%1.3fkHz -- I2C config",
                            g_I2C_deviceAddress7, (g_I2C_deviceAddress7 << 1), g_I2C_SCL_Hz,
                            (g_I2C_SCL_Hz / 1000.));
    cmdLine.serial().printf("\r\n %%IW byte byte ... byte RD=? ADDR=0x%2.2x -- I2C write/read",
                            g_I2C_deviceAddress7);
    //
#if SUPPORT_I2C
    // Menu ^ cmd=?) i2c_smbus_read_word_data
    cmdLine.serial().printf("\r\n %%I^ cmd=? -- i2c_smbus_read_word_data");
    // test low-level I2C i2c_smbus_read_word_data
#endif // SUPPORT_I2C
    //cmdLine.serial().printf(" H) Hunt for attached I2C devices");
    cmdLine.serial().printf("\r\n %%IP -- I2C Probe for attached devices");
    // cmdLine.serial().printf(" s) search i2c address");
#endif // SUPPORT_I2C

#if HAS_SPI // SUPPORT_SPI
    // TODO: support SPI HAS_SPI // SUPPORT_SPI
    // SPI test command  S (mosiData)+
    // %S... -- SPI diagnostics
    // %SC sclk=1Mhz -- SPI configure
    // %SW -- write (write and read)
    // %SR -- read (alias for %SW because SPI always write and read)
    // spi.format(8,0); // int bits_must_be_8, int mode=0_3 CPOL=0,CPHA=0 rising edge (initial default)
    // spi.format(8,1); // int bits_must_be_8, int mode=0_3 CPOL=0,CPHA=1 falling edge (initial default)
    // spi.format(8,2); // int bits_must_be_8, int mode=0_3 CPOL=1,CPHA=0 falling edge (initial default)
    // spi.format(8,3); // int bits_must_be_8, int mode=0_3 CPOL=1,CPHA=1 rising edge (initial default)
    // spi.frequency(1000000); // int SCLK_Hz=1000000 = 1MHz (initial default)
    // mode | POL PHA
    // -----+--------
    //   0  |  0   0
    //   1  |  0   1
    //   2  |  1   0
    //   3  |  1   1
    //cmdLine.serial().printf(" S) SPI mosi,mosi,...mosi hex bytes SCLK=1000000 CPOL=0 CPHA=0");
    // fixed: mbed-os-5.11: [Warning] format '%d' expects argument of type 'int', but argument 3 has type 'uint32_t {aka long unsigned int}' [-Wformat=]
    cmdLine.serial().printf("\r\n %%SC SCLK=%ld=%1.3fMHz CPOL=%d CPHA=%d -- SPI config",
                            g_SPI_SCLK_Hz, (g_SPI_SCLK_Hz / 1000000.),
                            ((g_SPI_dataMode & SPI_MODE2) ? 1 : 0),
                            ((g_SPI_dataMode & SPI_MODE1) ? 1 : 0));
    cmdLine.serial().printf("\r\n %%SD -- SPI diagnostic messages ");
    if (g_MAX11410_device.onSPIprint) {
        cmdLine.serial().printf("hide");
    }
    else {
        cmdLine.serial().printf("show");
    }
    cmdLine.serial().printf("\r\n %%SW mosi,mosi,...mosi -- SPI write hex bytes");
    // VERIFY: parse new SPI settings parse_strCommandArgs() SCLK=1000000 CPOL=0 CPHA=0
#endif // SUPPORT_SPI
       //
       // Application-specific commands (help text) here
       //
#if APPLICATION_ArduinoPinsMonitor
    cmdLine.serial().printf("\r\n A-Z,a-z,0-9 -- reserved for application use");     // ArduinoPinsMonitor
#endif // APPLICATION_ArduinoPinsMonitor
       //

    //~ extern void MAX11410_menu_help(CmdLine & cmdLine); // defined in Test_Menu_MAX11410.cpp\n
    //~ MAX11410_menu_help(cmdLine);
}
#endif // USE_CMDLINE_MENUS support CmdLine command menus

#if USE_CMDLINE_MENUS // support CmdLine command menus

//--------------------------------------------------
void pinsMonitor_submenu_onEOLcommandParser(CmdLine& cmdLine)
{
    // % diagnostic commands submenu
    // %Hpin -- digital output high
    // %Lpin -- digital output low
    // %?pin -- digital input
    // %A %Apin -- analog input
    // %Ppin df=xx -- pwm output
    // %Wpin -- measure high pulsewidth input in usec
    // %wpin -- measure low pulsewidth input in usec
    // %I... -- I2C diagnostics
    // %IP -- I2C probe
    // %IC scl=100khz ADDR=? -- I2C configure
    // %IW byte byte ... byte RD=? ADDR=0x -- write
    // %IR ADDR=? RD=? -- read
    // %I^ cmd=? -- i2c_smbus_read_word_data
    // %S... -- SPI diagnostics
    // %SC sclk=1Mhz -- SPI configure
    // %SW -- write (write and read)
    // %SR -- read (alias for %SW because SPI always write and read)
    // A-Z,a-z,0-9 reserved for application use
    //
    char strPinIndex[3];
    strPinIndex[0] = cmdLine[2];
    strPinIndex[1] = cmdLine[3];
    strPinIndex[2] = '\0';
    int pinIndex = strtoul(strPinIndex, NULL, 10);         // strtol(str, NULL, 10): get decimal value
    //cmdLine.serial().printf(" pinIndex=%d ", pinIndex);
    //
    // get next character
    switch (cmdLine[1])
    {
//--------------------------------------------------
#if USE_DATALOGGER_CommandTable // Run_command_table(onButton1_command_table)
// command_table: list of commands to perform on button press
        case 'B': case 'b':
        {
            int command_table_button_index = 1;
            // %B1 submenu for Button1
            // future: %B2 submenu for Button2
            // future: %B3 submenu for Button3
#if HAS_BUTTON1_DEMO_INTERRUPT
            // cmdLine[2] == '1' selects onButton1_command_table
            char** command_table = onButton1_command_table;
#endif
            switch(cmdLine[2])
            {
                default:
                case '1': case 'A': case 'a': case '!':
#if HAS_BUTTON1_DEMO_INTERRUPT
                    command_table_button_index = 1;
                    command_table = onButton1_command_table;
#endif
                    break;
                case '2': case 'B': case 'b': case '@':
#if HAS_BUTTON2_DEMO_INTERRUPT
                    command_table_button_index = 2;
                    command_table = onButton2_command_table;
#endif
                    break;
                case '3': case 'C': case 'c': case '#':
#if HAS_BUTTON3_DEMO_INTERRUPT
                    command_table_button_index = 3;
                    command_table = onButton3_command_table;
#endif
                    break;
                case '4':
#if HAS_BUTTON4_DEMO_INTERRUPT
                    command_table_button_index = 4;
                    command_table = onButton4_command_table;
#endif
                    break;
                case '5':
#if HAS_BUTTON5_DEMO_INTERRUPT
                    command_table_button_index = 5;
                    command_table = onButton5_command_table;
#endif
                    break;
                case '6':
#if HAS_BUTTON6_DEMO_INTERRUPT
                    command_table_button_index = 6;
                    command_table = onButton6_command_table;
#endif
                    break;
                case '7':
#if HAS_BUTTON7_DEMO_INTERRUPT
                    command_table_button_index = 7;
                    command_table = onButton7_command_table;
#endif
                    break;
                case '8':
#if HAS_BUTTON8_DEMO_INTERRUPT
                    command_table_button_index = 8;
                    command_table = onButton8_command_table;
#endif
                    break;
                case '9':
#if HAS_BUTTON9_DEMO_INTERRUPT
                    command_table_button_index = 9;
                    command_table = onButton9_command_table;
#endif
                    break;
            }
            //
            switch(cmdLine[3])
            {
            case '@':
                {
                    // %B1@ view/edit command table, similar to L@ for action table
                    int editRowIndex = 0;
                    int command_table_row_count = 0;
                    for (int lineIndex = 0; lineIndex < COMMAND_TABLE_ROW_MAX; lineIndex++) {
                        if (command_table[lineIndex] == NULL) { break; }
                        if (command_table[lineIndex][0] == '\0') { break; }
                        command_table_row_count = lineIndex+1;
                    }
                    //
                    // ignore extra spaces before the command
                    // find argIndex such that cmdLine[argIndex] is the start of the second word
                    int argIndex;
                    for (argIndex = 5; cmdLine[argIndex] != '\0'; argIndex++)
                    {
                        if (cmdLine[argIndex] == ' ') break;
                    }
                    for (; cmdLine[argIndex] != '\0'; argIndex++)
                    {
                        if (cmdLine[argIndex] != ' ') break;
                    }
                    //
                    // Edit the contents of command_table
                    switch (cmdLine[4]) // %B1@... -- Edit the contents of command_table
                    {
                        case '0': case '1': case '2': case '3': case '4':
                        case '5': case '6': case '7': case '8': case '9':
                            // %B1@nnn command replace row number nnn
                            // edit row data
                            // get row number to edit from cmdLine[4]
                            editRowIndex = atoi(cmdLine.str()+4);
                            if (command_table_row_count > 0) {
                                // if Datalogger_action_table_row_count == 0 then the table is empty
                                if ((editRowIndex >= 0) && (editRowIndex < command_table_row_count)) {
                                    // avoid replacing with null because that would truncate the table
                                    if (*(cmdLine.str()+argIndex)=='\0') {
                                        cmdLine.serial().printf("\r\n cannot replace row %d with nothing",
                                            editRowIndex);
                                    }
                                    else
                                    {
                                        // update row
                                        cmdLine.serial().printf("\r\n replace row %d with \"%s\"",
                                            editRowIndex, cmdLine.str()+argIndex);
                                        strncpy(command_table[editRowIndex], cmdLine.str()+argIndex, COMMAND_TABLE_COL_MAX-1);
                                        command_table[editRowIndex][COMMAND_TABLE_COL_MAX-1]='\0'; // null character at end of line
                                    }
                                }
                            } // end if (command_table_row_count > 0)
                            if ((editRowIndex == command_table_row_count) && (command_table_row_count < COMMAND_TABLE_ROW_MAX)) {
                                // %B1@nnn command add new row (even though this looks like a replace command) if and only if nnn==next new unassigned line number
                                //
                                command_table_row_count++;
                                cmdLine.serial().printf("\r\n add next row %d containing \"%s\"",
                                    editRowIndex, cmdLine.str()+argIndex);
                                strncpy(command_table[editRowIndex], cmdLine.str()+argIndex, COMMAND_TABLE_COL_MAX-1);
                                command_table[editRowIndex][COMMAND_TABLE_COL_MAX-1]='\0'; // null character at end of line
                                command_table[command_table_row_count][0]='\0'; // empty string marks end of command_table
                                //
                            }
                            //
                            break;
                        case '+':
                            // %B1@+ command add new row (if there is room to add)
                            // add a new row at end of table
                            if (command_table_row_count < COMMAND_TABLE_ROW_MAX) {
                                // if command_table_row_count => COMMAND_TABLE_ROW_MAX then the table is full
                                editRowIndex = command_table_row_count;
                                // avoid replacing with null because that would truncate the table
                                if (*(cmdLine.str()+argIndex)=='\0') {
                                    cmdLine.serial().printf("\r\n cannot add new row %d containing nothing",
                                        editRowIndex);
                                }
                                else
                                {
                                    command_table_row_count++;
                                    cmdLine.serial().printf("\r\n add new row %d containing \"%s\"",
                                        editRowIndex, cmdLine.str()+argIndex);
                                    strncpy(command_table[editRowIndex], cmdLine.str()+argIndex, COMMAND_TABLE_COL_MAX-1);
                                    command_table[editRowIndex][COMMAND_TABLE_COL_MAX-1]='\0'; // null character at end of line
                                    command_table[command_table_row_count][0]='\0'; // empty string marks end of command_table
                                }
                            }
                            break;
                        case '-':
                            // %B1@-nnn delete row number nnn (if list is not empty)
                            // delete row from table
                            if (command_table_row_count > 0) {
                                // if command_table_row_count == 0 then the table is empty
                                if ((cmdLine[5] == '~') && (cmdLine[6] == '~') && (cmdLine[7] == '~')) {
                                    // %B1@-~~~ clear entire command table
                                    cmdLine.serial().printf("\r\n clear entire command table");
                                    command_table_row_count = 0;
                                    command_table[command_table_row_count][0]='\0'; // empty string marks end of command_table
                                    break;
                                }
                                else
                                {
                                    // %B1@-nnn delete row number nnn (if list is not empty)
                                    editRowIndex = atoi(cmdLine.str()+5);
                                    if ((editRowIndex >= 0) && (editRowIndex < command_table_row_count)) {
                                        cmdLine.serial().printf("\r\n delete row %d", editRowIndex);
                                        // delete row editRowIndex from Datalogger_action_table
                                        for (int i = editRowIndex; i < (command_table_row_count-1); i++)
                                        {
                                            // copy row i+1 into row i
                                            cmdLine.serial().printf("\r\n copy row %d into row %d: \"%s\"",
                                                i+1, i, command_table[i+1]);
                                            strncpy(command_table[i], command_table[i+1], COMMAND_TABLE_COL_MAX-1);
                                            command_table[i][COMMAND_TABLE_COL_MAX-1]='\0'; // null character at end of line
                                        }
                                        command_table_row_count--;
                                        command_table[command_table_row_count][0]='\0'; // empty string marks end of command_table
                                    }
                                }
                            }
                            break;
                        case '.': // something other than ! because %B1! means trigger event
                            // pause the button event
                            // TODO: %B1@. disable button response but keep command table contents
                            //~ Datalogger_action_table_enabled = false;
                            break;
                        case '@':
                            // enable the button event
                            // TODO: %B1@@ enable button response
                            //~ Datalogger_action_table_enabled = true;
                            break;
                    }
                    //
                    // Print the contents of command_table
                    for (int lineIndex = 0; lineIndex < COMMAND_TABLE_ROW_MAX; lineIndex++) {
                        if (command_table[lineIndex] == NULL) { break; }
                        if (command_table[lineIndex][0] == '\0') { break; }
                        command_table_row_count = lineIndex+1;
                        cmdLine.serial().printf("\r\n %%B%1d@%d %s",    
                            command_table_button_index,
                            lineIndex,
                            command_table[lineIndex]);
                    }
                    //~ cmdLine.serial().printf("\r\n command_table_row_count = %d/%d",
                        //~ command_table_row_count, COMMAND_TABLE_ROW_MAX);
                    cmdLine.serial().printf("\r\n\r\nEdit Button%d event (used %d/%d, %d free):",
                        command_table_button_index,
                        command_table_row_count,
                        COMMAND_TABLE_ROW_MAX,
                        (COMMAND_TABLE_ROW_MAX - command_table_row_count)
                        );
                    if (command_table_row_count < COMMAND_TABLE_ROW_MAX) {
                        // if command_table_row_count => COMMAND_TABLE_ROW_MAX then the table is full
                        cmdLine.serial().printf("\r\n %%B%1d@+ command -- add new row %d at end of table",
                            command_table_button_index,
                            command_table_row_count);
                    }
                    if (command_table_row_count > 0) {
                        // if command_table_row_count == 0 then the table is empty
                        cmdLine.serial().printf("\r\n %%B%1d@%d command -- replace row %d", 
                            command_table_button_index,
                            command_table_row_count-1,
                            command_table_row_count-1);
                        cmdLine.serial().printf("\r\n %%B%1d@-%d -- delete row %d", 
                            command_table_button_index,
                            command_table_row_count-1,
                            command_table_row_count-1);
                        cmdLine.serial().printf("\r\n %%B%1d@-~~~ -- delete all rows",
                            command_table_button_index);
                        //~ cmdLine.serial().printf("\r\n %%B%1d@. -- pause entire command table",
                            //~ command_table_button_index);
                        //~ cmdLine.serial().printf("\r\n %%B%1d@@ -- enable command table",
                            //~ command_table_button_index);
                        cmdLine.serial().printf("\r\n %%B%1d! -- trigger Button%d event",
                            command_table_button_index,
                            command_table_button_index);
                    }
                    //
                } // case '@' -- %B1@ view/edit command table
                break;
            case '!':
                // TODO: %B1! trigger onButton1FallingEdge() immediately (for test development)
                Run_command_table(command_table);
                break;
            }
        }
        break;
#endif // USE_DATALOGGER_CommandTable
//--------------------------------------------------
#if HAS_digitalInOuts
        case 'H': case 'h':
        {
            // %Hpin -- digital output high
#if ARDUINO_STYLE
            pinMode(pinIndex, OUTPUT);             // digital pins 0, 1, 2, .. 13, analog input pins A0, A1, .. A5
            digitalWrite(pinIndex, HIGH);             // digital pins 0, 1, 2, .. 13, analog input pins A0, A1, .. A5
#else
            DigitalInOut& digitalInOutPin = find_digitalInOutPin(pinIndex);
            digitalInOutPin.output();
            digitalInOutPin.write(1);
#endif
            cmdLine.serial().printf(" digitalInOutPin %d Output High ", pinIndex);
        }
        break;
        case 'L': case 'l':
        {
            // %Lpin -- digital output low
#if ARDUINO_STYLE
            pinMode(pinIndex, OUTPUT);             // digital pins 0, 1, 2, .. 13, analog input pins A0, A1, .. A5
            digitalWrite(pinIndex, LOW);             // digital pins 0, 1, 2, .. 13, analog input pins A0, A1, .. A5
#else
            DigitalInOut& digitalInOutPin = find_digitalInOutPin(pinIndex);
            digitalInOutPin.output();
            digitalInOutPin.write(0);
#endif
            cmdLine.serial().printf(" digitalInOutPin %d Output Low ", pinIndex);
        }
        break;
        case '?':
        {
            // %?pin -- digital input
#if ARDUINO_STYLE
            pinMode(pinIndex, INPUT);             // digital pins 0, 1, 2, .. 13, analog input pins A0, A1, .. A5
#else
            DigitalInOut& digitalInOutPin = find_digitalInOutPin(pinIndex);
            digitalInOutPin.input();
#endif
            serial.printf(" digitalInOutPin %d Input ", pinIndex);
#if ARDUINO_STYLE
            int value = digitalRead(pinIndex);
#else
            int value = digitalInOutPin.read();
#endif
            cmdLine.serial().printf("%d ", value);
        }
        break;
#endif
        //
#if HAS_analogIns
        case 'A': case 'a':
        {
            // %A %Apin -- analog input
#if USE_Platform_AIN_Average
            // %A avg= -- set Platform_AIN_Average_N
            if (cmdLine.parse_int_dec("avg", Platform_AIN_Average_N))
            {
                // Platform_AIN_Average_N was updated
            }
#endif // USE_Platform_AIN_Average
#if HAS_Platform_AIN_Calibration
            // %A cal? view/export raw calibration values for all channels
            // double calibration_05_normValue_0_1 [NUM_PLATFORM_ANALOG_IN_CHANNELS] = {0.0, ... }
            // double calibration_05_V [NUM_PLATFORM_ANALOG_IN_CHANNELS] = {0.0, ... }
            // double calibration_95_normValue_0_1 [NUM_PLATFORM_ANALOG_IN_CHANNELS] = {0.0, ... }
            // double calibration_95_V [NUM_PLATFORM_ANALOG_IN_CHANNELS] = {0.0, ... }
            {
                char valueBuf[16];
                // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen);
                if (cmdLine.parse_and_remove_key("cal?", valueBuf, sizeof(valueBuf)))
                {
                    for (int ch = 0; ch < NUM_PLATFORM_ANALOG_IN_CHANNELS; ch++)
                    {
                        cmdLine.serial().printf(" %%A cal%dn=%1.9f cal%dv=%1.9fV cal%dn=%1.9f cal%dv=%1.9fV\r\n",
                            ch, calibration_05_normValue_0_1[ch],
                            ch, calibration_05_V[ch],
                            ch, calibration_95_normValue_0_1[ch],
                            ch, calibration_95_V[ch]
                        );
                    }
                    //
                    // print extended help for %A
                    // %A cal0n=0.02 cal0v=0.123 cal0n=0.938 cal0v=1.132 edit/import raw calibration values for any/all channels
                    //~ cmdLine.serial().printf("\r\n %%A cal0n=0.02 cal0v=0.123 cal0n=0.938 cal0v=1.132 -- update calibration");
                    // %A cal0test=0.5 test calibration functions by calculating the calibrated voltage of specified normValue_0_1
                    cmdLine.serial().printf(" %%A cal0test=0.5 -- calculate voltage at 50%% of full scale");
                    cmdLine.serial().printf("\r\n %%A v0cal=1.000V -- calibrate channel against a known input voltage");
                    // TODO: %A___TBD___ save calibration values in non-volatile memory on-chip (if supported)
                    // TODO: %A___TBD___ load calibration values from non-volatile memory on-chip (if supported)
                    // %A calreset -- reset all calibration data
                    cmdLine.serial().printf("\r\n %%A calreset -- reset all calibration data");
#if USE_Platform_AIN_Average
                    // %A avg=%d -- help string display Platform_AIN_Average_N
                    cmdLine.serial().printf("\r\n %%A avg=%d -- average", Platform_AIN_Average_N);
#endif // USE_Platform_AIN_Average
                    cmdLine.serial().printf("\r\n\r\n");
                    //
                } // end if (cmdLine.parse_and_remove_key("cal?", valueBuf, sizeof(valueBuf)))
            }
#endif // HAS_Platform_AIN_Calibration
#if HAS_Platform_AIN_Calibration
            {
                // %A calreset -- reset all calibration data
                char valueBuf[16];
                // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen);
                if (cmdLine.parse_and_remove_key("calreset", valueBuf, sizeof(valueBuf)))
                {
                    // nominal 5% fullscale point; normValue_0_1 < 0.5
                    calibration_05_normValue_0_1[0] = 0.25; calibration_05_V[0] = 0.3;
                    calibration_05_normValue_0_1[1] = 0.25; calibration_05_V[1] = 0.3;
                    calibration_05_normValue_0_1[2] = 0.25; calibration_05_V[2] = 0.3;
                    calibration_05_normValue_0_1[3] = 0.25; calibration_05_V[3] = 0.3;
                    calibration_05_normValue_0_1[4] = 0.25; calibration_05_V[4] = 1.5;
                    calibration_05_normValue_0_1[5] = 0.25; calibration_05_V[5] = 1.5;
                    //
                    // nominal 95% fullscale point; normValue_0_1 > 0.5
                    calibration_95_normValue_0_1[0] = 0.75; calibration_95_V[0] = 0.9;
                    calibration_95_normValue_0_1[1] = 0.75; calibration_95_V[1] = 0.9;
                    calibration_95_normValue_0_1[2] = 0.75; calibration_95_V[2] = 0.9;
                    calibration_95_normValue_0_1[3] = 0.75; calibration_95_V[3] = 0.9;
                    calibration_95_normValue_0_1[4] = 0.75; calibration_95_V[4] = 4.5;
                    calibration_95_normValue_0_1[5] = 0.75; calibration_95_V[5] = 4.5;
                    //
                }
            }
#endif // HAS_Platform_AIN_Calibration
#if HAS_Platform_AIN_Calibration
            // %A cal0n=0.02 cal0v=0.123 cal0n=0.938 cal0v=1.132 edit/import raw calibration values for any/all channels
            // %A cal4n=0.135874882 cal4v=0.800000012V cal4n=0.286168128 cal4v=1.700000048V
            // double calibration_05_normValue_0_1 [NUM_PLATFORM_ANALOG_IN_CHANNELS] = {0.0, ... }
            // double calibration_05_V [NUM_PLATFORM_ANALOG_IN_CHANNELS] = {0.0, ... }
            // double calibration_95_normValue_0_1 [NUM_PLATFORM_ANALOG_IN_CHANNELS] = {0.0, ... }
            // double calibration_95_V [NUM_PLATFORM_ANALOG_IN_CHANNELS] = {0.0, ... }
            {
                double normValue_0_1, V;
                for (int ch = 0; ch < NUM_PLATFORM_ANALOG_IN_CHANNELS; ch++)
                {
                    static char key_can0n[8] = "cal0n__";
                    sprintf(key_can0n, "cal%1dn", ch);
                    static char key_cal0v[8] = "cal0v__";
                    sprintf(key_cal0v, "cal%1dv", ch);
                    // first point could be the 5% point or the 95% point
                    double norm_threshold = (calibration_05_normValue_0_1[ch] + calibration_95_normValue_0_1[ch]) / 2.0;
                    bool updated_05 = false;
                    //~ bool updated_95 = false;
                    if (cmdLine.parse_double(key_can0n, normValue_0_1)) {
                        if (cmdLine.parse_double(key_cal0v, V))
                        {
                            if (normValue_0_1 < norm_threshold) {
                                // store first calibration point in the 5% slot
                                calibration_05_normValue_0_1[ch] = normValue_0_1;
                                calibration_05_V[ch] = V;
                                updated_05 = true;
                            }
                            else {
                                // store first calibration point in the 95% slot
                                calibration_95_normValue_0_1[ch] = normValue_0_1;
                                calibration_95_V[ch] = V;
                                //~ updated_95 = true;
                            }
                        }
                    }
                    // handle the optional second calibration point
                    if (cmdLine.parse_double(key_can0n, normValue_0_1))
                    {
                        if (cmdLine.parse_double(key_cal0v, V))
                        {
                            if (updated_05) {
                                // we already stored the first point here
                                // calibration_05_normValue_0_1[ch] = normValue_0_1;
                                // calibration_05_V[ch] = V;
                                // store the second point in the other slot
                                calibration_95_normValue_0_1[ch] = normValue_0_1;
                                calibration_95_V[ch] = V;
                            }
                            else {
                                // we already stored the first point here
                                // calibration_95_normValue_0_1[ch] = normValue_0_1;
                                // calibration_95_V[ch] = V;
                                // store the second point in the other slot
                                calibration_05_normValue_0_1[ch] = normValue_0_1;
                                calibration_05_V[ch] = V;
                            }
                        }
                    }
                    // make sure calibration_05_normValue_0_1[ch] < calibration_95_normValue_0_1[ch]
                    if (calibration_05_normValue_0_1[ch] > calibration_95_normValue_0_1[ch])
                    {
                        // swap to make sure calibration_05_normValue_0_1[ch] < calibration_95_normValue_0_1[ch]
                        double n05 = calibration_95_normValue_0_1[ch];
                        double V05 = calibration_95_V[ch];
                        double n95 = calibration_05_normValue_0_1[ch];
                        double V95 = calibration_05_V[ch];
                        calibration_05_normValue_0_1[ch] = n05;
                        calibration_05_V[ch] = V05;
                        calibration_95_normValue_0_1[ch] = n95;
                        calibration_95_V[ch] = V95;
                    }
                } // end for (int ch = 0; ch < NUM_PLATFORM_ANALOG_IN_CHANNELS; ch++)
            }
#endif // HAS_Platform_AIN_Calibration
#if HAS_Platform_AIN_Calibration
            // %A cal0test=0.5 test calibration functions by calculating the calibrated voltage of specified normValue_0_1
            // double CalibratedNormValue(int channel_0_5, double raw_normValue_0_1);
            for (int ch = 0; ch < NUM_PLATFORM_ANALOG_IN_CHANNELS; ch++)
            {
                static char key_cal0test[12] = "cal0test__";
                sprintf(key_cal0test, "cal%1dtest", ch);
                double normValue_0_1, Vtest;
                if (cmdLine.parse_double(key_cal0test, Vtest))
                {
                    // divide Vtest/adc_full_scale_voltage[0] to get normValue_0_1
                    normValue_0_1 = Vtest/adc_full_scale_voltage[ch];
                    //
                    cmdLine.serial().printf(" CalibratedNormValue(%d, %1.6f) = %1.6f = %1.6fV\r\n",
                        ch,
                        normValue_0_1,
                        CalibratedNormValue(ch, normValue_0_1),
                        (CalibratedNormValue(ch, normValue_0_1) * adc_full_scale_voltage[ch])
                    );
                    //
                    // sweep CalibratedNormValue argument
                    double normValue_0_1_start = -0.05;
                    double normValue_0_1_step = 0.05;
                    double normValue_0_1_end = 1.07;
                    for (normValue_0_1 = normValue_0_1_start;
                        normValue_0_1 <= normValue_0_1_end;
                        normValue_0_1 = normValue_0_1 + normValue_0_1_step)
                    {
                        cmdLine.serial().printf(" CalibratedNormValue(%d, %1.6f) = %1.6f = %1.6fV\r\n",
                            ch, 
                            normValue_0_1, 
                            CalibratedNormValue(ch, normValue_0_1),
                            (CalibratedNormValue(ch, normValue_0_1) * adc_full_scale_voltage[ch])
                        );
                    }
                }
            } // end for (int ch = 0; ch < NUM_PLATFORM_ANALOG_IN_CHANNELS; ch++)
#endif // HAS_Platform_AIN_Calibration
#if HAS_Platform_AIN_Calibration
            // %A v0cal=1.000V -- calibrate channel against a known input voltage
            // double calibration_05_normValue_0_1 [NUM_PLATFORM_ANALOG_IN_CHANNELS] = {0.0, ... }
            // double calibration_05_V [NUM_PLATFORM_ANALOG_IN_CHANNELS] = {0.0, ... }
            // double calibration_95_normValue_0_1 [NUM_PLATFORM_ANALOG_IN_CHANNELS] = {0.0, ... }
            // double calibration_95_V [NUM_PLATFORM_ANALOG_IN_CHANNELS] = {0.0, ... }
            for (int ch = 0; ch < NUM_PLATFORM_ANALOG_IN_CHANNELS; ch++)
            {
                static char key_v0cal[10] = "v0cal__";
                sprintf(key_v0cal, "v%1dcal", ch);
                double Vtest;
                if (cmdLine.parse_double(key_v0cal, Vtest))
                {
                    // divide Vtest/adc_full_scale_voltage[0] to get normValue_0_1
                    double Vtest_normValue_0_1 = Vtest/adc_full_scale_voltage[ch];
                    //
                    // %A v0cal=1.000 calibrate one channel by measuring against a known input voltage
                    //
                    cmdLine.serial().printf("\r\n Measuring against input voltage of %1.6fV...", Vtest);
#if USE_Platform_AIN_Average
                    const int numberOfMeasurements = 16 * Platform_AIN_Average_N;
#else  // USE_Platform_AIN_Average
                    const int numberOfMeasurements = 16;
#endif // USE_Platform_AIN_Average
                    double Sx_measure_normValue_0_1 = 0; // sum of values
                    double Sxx_measure_normValue_0_1 = 0; // sum of the squares of the values
                    for (int count = 0; count < numberOfMeasurements; count++)
                    {
                        double measure_normValue_0_1;
                        switch(ch)
                        {    
                        case 0: measure_normValue_0_1 = analogIn0.read(); break;
                        case 1: measure_normValue_0_1 = analogIn1.read(); break;
                        case 2: measure_normValue_0_1 = analogIn2.read(); break;
                        case 3: measure_normValue_0_1 = analogIn3.read(); break;
                        case 4: measure_normValue_0_1 = analogIn4.read(); break;
                        case 5: measure_normValue_0_1 = analogIn5.read(); break;
                        }
                        Sx_measure_normValue_0_1 = Sx_measure_normValue_0_1 + measure_normValue_0_1;
                        Sxx_measure_normValue_0_1 = Sxx_measure_normValue_0_1 + (measure_normValue_0_1 * measure_normValue_0_1);
                        if ((count < 3) || (count >= numberOfMeasurements - 3)) {
                            cmdLine.serial().printf("\r\n raw: %7.6f%% = %1.6fV",
                                measure_normValue_0_1 * 100.0,
                                (measure_normValue_0_1 * adc_full_scale_voltage[ch])
                            );
                        }
                    }
                    cmdLine.serial().printf("\r\n numberOfMeasurements = %d", numberOfMeasurements);
                    cmdLine.serial().printf("\r\n Sx_measure_normValue_0_1 = %1.6f", Sx_measure_normValue_0_1);
                    cmdLine.serial().printf("\r\n Sxx_measure_normValue_0_1 = %1.6f", Sxx_measure_normValue_0_1);
                    //
                    // calculate mean_measure_normValue_0_1 from
                    // Sxx_measure_normValue_0_1, Sx_measure_normValue_0_1, numberOfMeasurements
                    double mean_measure_normValue_0_1 = Sx_measure_normValue_0_1 / numberOfMeasurements;
                    cmdLine.serial().printf("\r\n mean = %7.6f%% = %1.6fV",
                        mean_measure_normValue_0_1 * 100.0,
                        (mean_measure_normValue_0_1 * adc_full_scale_voltage[ch])
                    );
                    //
                    // TODO: calculate sample variance_measure_normValue_0_1 from
                    // Sxx_measure_normValue_0_1, Sx_measure_normValue_0_1, numberOfMeasurements
                    // variance_measure_normValue_0_1 = (Sxx - ( Sx * Sx / nWords) ) / (nWords - 1);
                    double variance_measure_normValue_0_1 = (Sxx_measure_normValue_0_1 - ( Sx_measure_normValue_0_1 * Sx_measure_normValue_0_1 / numberOfMeasurements) ) / (numberOfMeasurements - 1);
                    cmdLine.serial().printf("\r\n variance = %7.6f%% = %1.6fV",
                        variance_measure_normValue_0_1 * 100.0,
                        (variance_measure_normValue_0_1 * adc_full_scale_voltage[ch])
                    );
                    //
                    // calculate sample stddev_measure_normValue_0_1 from
                    // Sxx_measure_normValue_0_1, Sx_measure_normValue_0_1, numberOfMeasurements
                    double stddev_measure_normValue_0_1 = sqrt(variance_measure_normValue_0_1);
                    cmdLine.serial().printf("\r\n stddev = %7.6f%% = %1.6fV",
                        stddev_measure_normValue_0_1 * 100.0,
                        (stddev_measure_normValue_0_1 * adc_full_scale_voltage[ch])
                    );
                    //
                    // Validate the measurements:
                    // is the mean near the expected value?
                    // is the standard deviation not too high?
                    bool isCalibrationValid = true;
                    if ((Vtest_normValue_0_1 - mean_measure_normValue_0_1) > 0.1) {
                        isCalibrationValid = false;
                        cmdLine.serial().printf(" mean value too far from expected.\r\n");
                    }
                    if ((Vtest_normValue_0_1 - mean_measure_normValue_0_1) < -0.1) {
                        isCalibrationValid = false;
                        cmdLine.serial().printf(" mean value too far from expected.\r\n");
                    }
                    if ((stddev_measure_normValue_0_1) > 10) {
                        isCalibrationValid = false;
                        cmdLine.serial().printf(" stddev too high.\r\n");
                    }
                    if (isCalibrationValid)
                    {
                        // update calibration point (mean_measure_normValue_0_1, Vtest)
                        if (Vtest_normValue_0_1 < 0.5) {
                            calibration_05_normValue_0_1[ch] = mean_measure_normValue_0_1;
                            calibration_05_V[ch] = Vtest;
                        } else {
                            calibration_95_normValue_0_1[ch] = mean_measure_normValue_0_1;
                            calibration_95_V[ch] = Vtest;
                        }
                        //
                        // print updated calibration values
                        cmdLine.serial().printf("\r\n");
                        cmdLine.serial().printf(" %%A cal%dn=%1.9f cal%dv=%1.9fV cal%dn=%1.9f cal%dv=%1.9fV\r\n",
                            ch, calibration_05_normValue_0_1[ch],
                            ch, calibration_05_V[ch],
                            ch, calibration_95_normValue_0_1[ch],
                            ch, calibration_95_V[ch]
                        );
                        // print corrected value of mean of measurements (should be close to Vtest)
                        cmdLine.serial().printf(" CalibratedNormValue(%d, %1.6f) = %1.6f = %1.6fV vs %1.6fV\r\n",
                            ch,
                            mean_measure_normValue_0_1,
                            CalibratedNormValue(ch, mean_measure_normValue_0_1),
                            (CalibratedNormValue(ch, mean_measure_normValue_0_1) * adc_full_scale_voltage[ch]),
                            Vtest
                        );
                    } // end if (isCalibrationValid)
                    else {
                        cmdLine.serial().printf("\r\n");
                        cmdLine.serial().printf(" Measurement Error: Calibration will not be updated.\r\n");
                    } // end if (isCalibrationValid)
                } // end if key_v0cal
            } // end for (int ch = 0; ch < NUM_PLATFORM_ANALOG_IN_CHANNELS; ch++)
#endif // HAS_Platform_AIN_Calibration
#if HAS_Platform_AIN_Calibration
            // // TODO: %A___TBD___ save calibration values in non-volatile memory on-chip (if supported)
            // {
            //     char valueBuf[16];
            //     // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen);
            //     if (cmdLine.parse_and_remove_key("__TBD_saveCal__", valueBuf, sizeof(valueBuf)))
            //     {
            //         // handle %A __TBD_saveCal__ -- command
            //     }
            // }
#endif // HAS_Platform_AIN_Calibration
#if HAS_Platform_AIN_Calibration
            // // TODO: %A___TBD___ load calibration values from non-volatile memory on-chip (if supported)
            // {
            //     char valueBuf[16];
            //     // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen);
            //     if (cmdLine.parse_and_remove_key("__TBD_loadCal__", valueBuf, sizeof(valueBuf)))
            //     {
            //         // handle %A __TBD_loadCal__ -- command
            //     }
            // }
#endif // HAS_Platform_AIN_Calibration
            // %A -- report analog input voltages
#if USE_Platform_AIN_Average
            // #if USE_Platform_AIN_Average -- for (i=0; i<Platform_AIN_Average_N; i++) -- analogInPin.read()
#endif // USE_Platform_AIN_Average
#if analogIn4_IS_HIGH_RANGE_OF_analogIn0
            // Platform board uses AIN4,AIN5,.. as high range of AIN0,AIN1,..
            for (int pinIndex = 0; pinIndex < 2; pinIndex++)
            {
                int cPinIndex = '0' + pinIndex;
                AnalogIn& analogInPin = find_analogInPin(cPinIndex);
                float adc_full_scale_voltage = analogInPin_fullScaleVoltage[pinIndex];
#if USE_Platform_AIN_Average
                // #if USE_Platform_AIN_Average -- for (i=0; i<Platform_AIN_Average_N; i++) -- analogInPin.read()
                // float normValue_0_1 = analogInPin.read(); but mean of Platform_AIN_Average_N samples
                float normValue_0_1 = 0;
                for (int i = 0; i < Platform_AIN_Average_N; i++) {
                    normValue_0_1 = normValue_0_1 + analogInPin.read();
                }
                normValue_0_1 = normValue_0_1 / Platform_AIN_Average_N;
#else  // USE_Platform_AIN_Average
                float normValue_0_1 = analogInPin.read();
#endif // USE_Platform_AIN_Average
#if HAS_Platform_AIN_Calibration
                // apply calibration to normValue_0_1 value
                normValue_0_1 = CalibratedNormValue(pinIndex, normValue_0_1);
#endif // HAS_Platform_AIN_Calibration
                //
                int pinIndexH = pinIndex + 4;
                int cPinIndexH = '0' + pinIndexH;
                AnalogIn& analogInPinH = find_analogInPin(cPinIndexH);
                float adc_full_scale_voltageH = analogInPin_fullScaleVoltage[pinIndexH];
#if USE_Platform_AIN_Average
                // #if USE_Platform_AIN_Average -- for (i=0; i<Platform_AIN_Average_N; i++) -- analogInPin.read()
                // float normValueH_0_1 = analogInPinH.read(); but mean of Platform_AIN_Average_N samples
                float normValueH_0_1 = 0;
                for (int i = 0; i < Platform_AIN_Average_N; i++) {
                    normValueH_0_1 = normValueH_0_1 + analogInPinH.read();
                }
                normValueH_0_1 = normValueH_0_1 / Platform_AIN_Average_N;
#else  // USE_Platform_AIN_Average
                float normValueH_0_1 = analogInPinH.read();
#endif // USE_Platform_AIN_Average
#if HAS_Platform_AIN_Calibration
                // apply calibration to normValue_0_1 value
                normValueH_0_1 = CalibratedNormValue(pinIndex, normValueH_0_1);
#endif // HAS_Platform_AIN_Calibration
                //
                cmdLine.serial().printf("A%c = %7.3f%% = %1.3fV  high range A%c = %7.3f%% = %1.3fV  \r\n",
                                        cPinIndex,
                                        normValue_0_1 * 100.0,
                                        normValue_0_1 * adc_full_scale_voltage,
                                        cPinIndexH,
                                        normValueH_0_1 * 100.0,
                                        normValueH_0_1 * adc_full_scale_voltageH
                                        );
            }
            for (int pinIndex = 2; pinIndex < 4; pinIndex++)
            {
                int cPinIndex = '0' + pinIndex;
                AnalogIn& analogInPin = find_analogInPin(cPinIndex);
                float adc_full_scale_voltage = analogInPin_fullScaleVoltage[pinIndex];
#if USE_Platform_AIN_Average
                // #if USE_Platform_AIN_Average -- for (i=0; i<Platform_AIN_Average_N; i++) -- analogInPin.read()
                // float normValue_0_1 = analogInPin.read(); but mean of Platform_AIN_Average_N samples
                float normValue_0_1 = 0;
                for (int i = 0; i < Platform_AIN_Average_N; i++) {
                    normValue_0_1 = normValue_0_1 + analogInPin.read();
                }
                normValue_0_1 = normValue_0_1 / Platform_AIN_Average_N;
#else  // USE_Platform_AIN_Average
                float normValue_0_1 = analogInPin.read();
#endif // USE_Platform_AIN_Average
#if HAS_Platform_AIN_Calibration
                // apply calibration to normValue_0_1 value
                normValue_0_1 = CalibratedNormValue(pinIndex, normValue_0_1);
#endif // HAS_Platform_AIN_Calibration
                //
                cmdLine.serial().printf("A%c = %7.3f%% = %1.3fV\r\n",
                                        cPinIndex,
                                        normValue_0_1 * 100.0,
                                        normValue_0_1 * adc_full_scale_voltage
                                        );
            }
#else // analogIn4_IS_HIGH_RANGE_OF_analogIn0
            // Platform board uses simple analog inputs
            // assume standard Arduino analog inputs A0-A5
            for (int pinIndex = 0; pinIndex < 6; pinIndex++)
            {
                int cPinIndex = '0' + pinIndex;
                AnalogIn& analogInPin = find_analogInPin(cPinIndex);
                float adc_full_scale_voltage = analogInPin_fullScaleVoltage[pinIndex];
#if USE_Platform_AIN_Average
                // #if USE_Platform_AIN_Average -- for (i=0; i<Platform_AIN_Average_N; i++) -- analogInPin.read()
                // float normValue_0_1 = analogInPin.read(); but mean of Platform_AIN_Average_N samples
                float normValue_0_1 = 0;
                for (int i = 0; i < Platform_AIN_Average_N; i++) {
                    normValue_0_1 = normValue_0_1 + analogInPin.read();
                }
                normValue_0_1 = normValue_0_1 / Platform_AIN_Average_N;
#else  // USE_Platform_AIN_Average
                float normValue_0_1 = analogInPin.read();
#endif // USE_Platform_AIN_Average
#if HAS_Platform_AIN_Calibration
                // apply calibration to normValue_0_1 value
                normValue_0_1 = CalibratedNormValue(pinIndex, normValue_0_1);
#endif // HAS_Platform_AIN_Calibration
                //
                cmdLine.serial().printf("A%c = %7.3f%% = %1.3fV\r\n",
                                        cPinIndex,
                                        normValue_0_1 * 100.0,
                                        normValue_0_1 * adc_full_scale_voltage
                                        );
            }
#endif // analogIn4_IS_HIGH_RANGE_OF_analogIn0
        }
        break;
#endif
        //
#if HAS_SPI2_MAX541
        case 'D': case 'd':
        {
            // %D -- DAC output MAX541 (SPI2) -- need cmdLine.parse_float(voltageV)
            // MAX541 max541(spi2_max541, spi2_max541_cs);
            float voltageV = max541.Get_Voltage();
            // if (cmdLine[2] == '+') {
            //     // %D+
            //     voltageV = voltageV * 1.25f;
            //     if (voltageV >= max541.VRef) voltageV = max541.VRef;
            //     SelfTest_MAX541_Voltage(cmdLine, max541, voltageV);
            // }
            // else if (cmdLine[2] == '-') {
            //     // %D-
            //     voltageV = voltageV * 0.75f;
            //     if (voltageV < 0.1f) voltageV = 0.1f;
            //     SelfTest_MAX541_Voltage(cmdLine, max541, voltageV);
            // }
            if (cmdLine.parse_float("V", voltageV))
            {
                // %D V=1.234 -- set voltage
                max541.Set_Voltage(voltageV);
            }
            else if (cmdLine.parse_float("TEST", voltageV))
            {
                // %D TEST=1.234 -- set voltage and compare with AIN0
                SelfTest_MAX541_Voltage(cmdLine, max541, voltageV);
            }
            else if (cmdLine.parse_float("CAL", voltageV))
            {
                // %D CAL=1.234 -- calibrate VRef and compare with AIN0

                max541.Set_Code(0x8000); // we don't know the fullscale voltage yet, so set code to midscale
                double max541_midscale_V = analogInPin_fullScaleVoltage[4] * analogIn4.read(); // TARGET_MAX32630 J1.5 AIN_4 = AIN0 / 5.0     fullscale is 6.0V
                const int average_count = 100;
                const double average_K = 0.25;
                for (int count = 0; count < average_count; count++) {
                    double measurement_V = analogInPin_fullScaleVoltage[4] * analogIn4.read(); // TARGET_MAX32630 J1.5 AIN_4 = AIN0 / 5.0     fullscale is 6.0V
                    max541_midscale_V = ((1 - average_K) * max541_midscale_V) + (average_K * measurement_V);
                }
                max541.VRef = 2.0 * max541_midscale_V;
                cmdLine.serial().printf(
                    "\r\n      MAX541 midscale = %1.3fV, so fullscale = %1.3fV",
                    max541_midscale_V, max541.VRef);
                // Detect whether MAX541 is really connected to MAX32625MBED.AIN0/AIN4
                voltageV = 1.0f;
                SelfTest_MAX541_Voltage(cmdLine, max541, voltageV);
            }
            else {
                // %D -- print MAX541 DAC status
                cmdLine.serial().printf("MAX541 code=0x%4.4x = %1.3fV  VRef=%1.3fV\r\n",
                                        max541.Get_Code(), max541.Get_Voltage(), max541.VRef);
            }
        }
        break;
#endif

        //
#if HAS_I2C // SUPPORT_I2C
        case 'I': case 'i':
            // %I... -- I2C diagnostics
            // %IP -- I2C probe
            // %IC scl=100khz ADDR=? -- I2C configure
            // %IW byte byte ... byte RD=? ADDR=0x -- write
            // %IR ADDR=? RD=? -- read
            // %I^ cmd=? -- i2c_smbus_read_word_data
            // get next character
            // TODO: parse cmdLine arg (ADDR=\d+)? --> g_I2C_deviceAddress7
            cmdLine.parse_byte_hex("ADDR", g_I2C_deviceAddress7);
            // TODO: parse cmdLine arg (RD=\d)? --> g_I2C_read_count
            g_I2C_read_count = 0;         // read count must be reset every command
            cmdLine.parse_byte_dec("RD", g_I2C_read_count);
            // TODO: parse cmdLine arg (CMD=\d)? --> g_I2C_command_regAddress
            cmdLine.parse_byte_hex("CMD", g_I2C_command_regAddress);
            switch (cmdLine[2])
            {
                case 'P': case 'p':
                {
                    // %IP -- I2C probe
                    HuntAttachedI2CDevices(cmdLine, 0x03, 0x77);
                }
                break;
                case 'C': case 'c':
                {
                    bool isUpdatedI2CConfig = false;
                    // %IC scl=100khz ADDR=? -- I2C configure
                    // parse cmdLine arg (SCL=\d+(kHZ|MHZ)?)? --> g_I2C_SCL_Hz
                    if (cmdLine.parse_frequency_Hz("SCL", g_I2C_SCL_Hz))
                    {
                        isUpdatedI2CConfig = true;
                        // TODO1: validate g_I2C_SCL_Hz against system clock frequency F_CPU
                        if (g_I2C_SCL_Hz > limit_max_I2C_SCL_Hz)
                        {
                            g_I2C_SCL_Hz = limit_max_I2C_SCL_Hz;
                        }
                        if (g_I2C_SCL_Hz < limit_min_I2C_SCL_Hz)
                        {
                            g_I2C_SCL_Hz = limit_min_I2C_SCL_Hz;
                        }
                    }
                    if (isUpdatedI2CConfig)
                    {
                        // declare in narrower scope: MAX32625MBED I2C i2cMaster(...)
                        I2C i2cMaster(I2C0_SDA, I2C0_SCL);             // sda scl TARGET_MAX32635MBED: P1_6, P1_7 Arduino 10-pin header
                        i2cMaster.frequency(g_I2C_SCL_Hz);
                        i2cMaster.start();
                        i2cMaster.stop();
                        i2cMaster.frequency(g_I2C_SCL_Hz);
                        cmdLine.serial().printf(
                            "\r\n %%IC ADDR=0x%2.2x=(0x%2.2x>>1) SCL=%d=%1.3fkHz -- I2C config",
                            g_I2C_deviceAddress7, (g_I2C_deviceAddress7 << 1), g_I2C_SCL_Hz,
                            (g_I2C_SCL_Hz / 1000.));
                        i2cMaster.start();
                        i2cMaster.stop();
                    }
                }
                break;
                case 'W': case 'w':
                {
                    // declare in narrower scope: MAX32625MBED I2C i2cMaster(...)
                    I2C i2cMaster(I2C0_SDA, I2C0_SCL);             // sda scl TARGET_MAX32635MBED: P1_6, P1_7 Arduino 10-pin header
                    i2cMaster.frequency(g_I2C_SCL_Hz);
                    // %IW byte byte ... byte RD=? ADDR=0x -- write
                    // parse cmdLine byte list --> int byteCount; int mosiData[MAX_SPI_BYTE_COUNT];
                    #define MAX_I2C_BYTE_COUNT 32
                    size_t byteCount = byteCount;
                    static char mosiData[MAX_I2C_BYTE_COUNT];
                    static char misoData[MAX_I2C_BYTE_COUNT];
                    if (cmdLine.parse_byteCount_byteList_hex(byteCount, mosiData,
                                                             MAX_I2C_BYTE_COUNT))
                    {
                        // hex dump mosiData[0..byteCount-1]
                        cmdLine.serial().printf(
                            "\r\nADDR=0x%2.2x=(0x%2.2x>>1) byteCount:%d RD=%d\r\nI2C MOSI->",
                            g_I2C_deviceAddress7,
                            (g_I2C_deviceAddress7 << 1), byteCount, g_I2C_read_count);
                        for (unsigned int byteIndex = 0; byteIndex < byteCount; byteIndex++)
                        {
                            cmdLine.serial().printf(" 0x%2.2X", mosiData[byteIndex]);
                        }
                        //
                        // TODO: i2c transfer
                        //const int addr7bit = 0x48;      // 7 bit I2C address
                        //const int addr8bit = 0x48 << 1; // 8bit I2C address, 0x90
                        // /* int  */   i2cMaster.read (int addr8bit, char *data, int length, bool repeated=false) // Read from an I2C slave.
                        // /* int  */   i2cMaster.read (int ack) // Read a single byte from the I2C bus.
                        // /* int  */   i2cMaster.write (int addr8bit, const char *data, int length, bool repeated=false) // Write to an I2C slave.
                        // /* int  */   i2cMaster.write (int data) // Write single byte out on the I2C bus.
                        // /* void */   i2cMaster.start (void) // Creates a start condition on the I2C bus.
                        // /* void */   i2cMaster.stop (void) // Creates a stop condition on the I2C bus.
                        // /* int */    i2cMaster.transfer (int addr8bit, const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length, const event_callback_t &callback, int event=I2C_EVENT_TRANSFER_COMPLETE, bool repeated=false) // Start nonblocking I2C transfer. More...
                        // /* void */   i2cMaster.abort_transfer () // Abort the ongoing I2C transfer. More...
                        const int addr8bit = g_I2C_deviceAddress7 << 1;             // 8bit I2C address, 0x90
                        unsigned int misoLength = 0;
                        bool repeated = (g_I2C_read_count > 0);
                        //
                        int writeStatus = i2cMaster.write (addr8bit, mosiData, byteCount, repeated);
                        switch (writeStatus)
                        {
                            case 0: cmdLine.serial().printf(" ack "); break;
                            case 1: cmdLine.serial().printf(" nack "); break;
                            default: cmdLine.serial().printf(" {writeStatus 0x%2.2X} ",
                                                             writeStatus);
                        }
                        if (repeated)
                        {
                            int readStatus =
                                i2cMaster.read (addr8bit, misoData, g_I2C_read_count, false);
                            switch (readStatus)
                            {
                                case 1: cmdLine.serial().printf(" nack "); break;
                                case 0: cmdLine.serial().printf(" ack "); break;
                                default: cmdLine.serial().printf(" {readStatus 0x%2.2X} ",
                                                                 readStatus);
                            }
                        }
                        //
                        if (misoLength > 0)
                        {
                            // hex dump misoData[0..byteCount-1]
                            cmdLine.serial().printf(" MISO<-");
                            for (unsigned int byteIndex = 0; byteIndex < g_I2C_read_count;
                                 byteIndex++)
                            {
                                cmdLine.serial().printf(" 0x%2.2X", misoData[byteIndex]);
                            }
                        }
                        cmdLine.serial().printf(" ");
                    }
                }
                break;
                case 'R': case 'r':
                {
                    // declare in narrower scope: MAX32625MBED I2C i2cMaster(...)
                    I2C i2cMaster(I2C0_SDA, I2C0_SCL);             // sda scl TARGET_MAX32635MBED: P1_6, P1_7 Arduino 10-pin header
                    i2cMaster.frequency(g_I2C_SCL_Hz);
                    // %IR ADDR=? RD=? -- read
                    // TODO: i2c transfer
                    //const int addr7bit = 0x48;      // 7 bit I2C address
                    //const int addr8bit = 0x48 << 1; // 8bit I2C address, 0x90
                    // /* int  */   i2cMaster.read (int addr8bit, char *data, int length, bool repeated=false) // Read from an I2C slave.
                    // /* int  */   i2cMaster.read (int ack) // Read a single byte from the I2C bus.
                    // /* int  */   i2cMaster.write (int addr8bit, const char *data, int length, bool repeated=false) // Write to an I2C slave.
                    // /* int  */   i2cMaster.write (int data) // Write single byte out on the I2C bus.
                    // /* void */   i2cMaster.start (void) // Creates a start condition on the I2C bus.
                    // /* void */   i2cMaster.stop (void) // Creates a stop condition on the I2C bus.
                    // /* int */    i2cMaster.transfer (int addr8bit, const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length, const event_callback_t &callback, int event=I2C_EVENT_TRANSFER_COMPLETE, bool repeated=false) // Start nonblocking I2C transfer. More...
                    // /* void */   i2cMaster.abort_transfer () // Abort the ongoing I2C transfer. More...
                }
                break;
                case '^':
                {
                    // declare in narrower scope: MAX32625MBED I2C i2cMaster(...)
                    I2C i2cMaster(I2C0_SDA, I2C0_SCL);             // sda scl TARGET_MAX32635MBED: P1_6, P1_7 Arduino 10-pin header
                    i2cMaster.frequency(g_I2C_SCL_Hz);
                    // %I^ cmd=? -- i2c_smbus_read_word_data
                    // TODO: i2c transfer
                    //const int addr7bit = 0x48;      // 7 bit I2C address
                    //const int addr8bit = 0x48 << 1; // 8bit I2C address, 0x90
                    // /* int  */   i2cMaster.read (int addr8bit, char *data, int length, bool repeated=false) // Read from an I2C slave.
                    // /* int  */   i2cMaster.read (int ack) // Read a single byte from the I2C bus.
                    // /* int  */   i2cMaster.write (int addr8bit, const char *data, int length, bool repeated=false) // Write to an I2C slave.
                    // /* int  */   i2cMaster.write (int data) // Write single byte out on the I2C bus.
                    // /* void */   i2cMaster.start (void) // Creates a start condition on the I2C bus.
                    // /* void */   i2cMaster.stop (void) // Creates a stop condition on the I2C bus.
                    // /* int */    i2cMaster.transfer (int addr8bit, const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length, const event_callback_t &callback, int event=I2C_EVENT_TRANSFER_COMPLETE, bool repeated=false) // Start nonblocking I2C transfer. More...
                    // /* void */   i2cMaster.abort_transfer () // Abort the ongoing I2C transfer. More...
                }
                break;
            }         // switch(cmdLine[2])
            break;
#endif
        //
#if HAS_SPI // SUPPORT_SPI
        case 'S': case 's':
        {
            // %S... -- SPI diagnostics
            // %SC sclk=1Mhz -- SPI configure
            // %SW -- write (write and read)
            // %SR -- read (alias for %SW because SPI always write and read)
            //
            // Process arguments SCLK=\d+(kHZ|MHZ) CPOL=\d CPHA=\d
            bool isUpdatedSPIConfig = false;
            // parse cmdLine arg (CPOL=\d)? --> g_SPI_dataMode | SPI_MODE2
            // parse cmdLine arg (CPHA=\d)? --> g_SPI_dataMode | SPI_MODE1
            if (cmdLine.parse_flag("CPOL", g_SPI_dataMode, SPI_MODE2))
            {
                isUpdatedSPIConfig = true;
            }
            if (cmdLine.parse_flag("CPHA", g_SPI_dataMode, SPI_MODE1))
            {
                isUpdatedSPIConfig = true;
            }
            if (cmdLine.parse_flag("CS", g_SPI_cs_state, 1))
            {
                isUpdatedSPIConfig = true;
            }
            // parse cmdLine arg (SCLK=\d+(kHZ|MHZ)?)? --> g_SPI_SCLK_Hz
            if (cmdLine.parse_frequency_Hz("SCLK", g_SPI_SCLK_Hz))
            {
                isUpdatedSPIConfig = true;
                // TODO1: validate g_SPI_SCLK_Hz against system clock frequency F_CPU
                if (g_SPI_SCLK_Hz > limit_max_SPI_SCLK_Hz)
                {
                    g_SPI_SCLK_Hz = limit_max_SPI_SCLK_Hz;
                }
                if (g_SPI_SCLK_Hz < limit_min_SPI_SCLK_Hz)
                {
                    g_SPI_SCLK_Hz = limit_min_SPI_SCLK_Hz;
                }
            }
            // Update SPI configuration
            if (isUpdatedSPIConfig)
            {
                // %SC sclk=1Mhz -- SPI configure
                spi_cs = g_SPI_cs_state;
                spi.format(8,g_SPI_dataMode);             // int bits_must_be_8, int mode=0_3 CPOL=0,CPHA=0
#if APPLICATION_MAX5715
                g_MAX5715_device.spi_frequency(g_SPI_SCLK_Hz);
#elif APPLICATION_MAX11131
                g_MAX11131_device.spi_frequency(g_SPI_SCLK_Hz);
#elif APPLICATION_MAX5171
                g_MAX5171_device.spi_frequency(g_SPI_SCLK_Hz);
#elif APPLICATION_MAX11410
                g_MAX11410_device.spi_frequency(g_SPI_SCLK_Hz);
#elif APPLICATION_MAX12345
                g_MAX12345_device.spi_frequency(g_SPI_SCLK_Hz);
#else
                spi.frequency(g_SPI_SCLK_Hz);             // int SCLK_Hz=1000000 = 1MHz (initial default)
#endif
                //
                double ideal_divisor = ((double)SystemCoreClock) / g_SPI_SCLK_Hz;
                int actual_divisor = (int)(ideal_divisor + 0.0);             // frequency divisor truncate
                double actual_SCLK_Hz = SystemCoreClock / actual_divisor;
                //
                // fixed: mbed-os-5.11: [Warning] format '%d' expects argument of type 'int', but argument 6 has type 'uint32_t {aka long unsigned int}' [-Wformat=]
                cmdLine.serial().printf(
                    "\r\n %%SC CPOL=%d CPHA=%d CS=%d SCLK=%ld=%1.3fMHz (%1.1fMHz/%1.2f = actual %1.3fMHz) -- SPI config",
                    ((g_SPI_dataMode & SPI_MODE2) ? 1 : 0),
                    ((g_SPI_dataMode & SPI_MODE1) ? 1 : 0),
                    g_SPI_cs_state,
                    g_SPI_SCLK_Hz,
                    (g_SPI_SCLK_Hz / 1000000.),
                    ((double)(SystemCoreClock / 1000000.)),
                    ideal_divisor,
                    (actual_SCLK_Hz / 1000000.)
                    );
            }
            // get next character
            switch (cmdLine[2])
            {
                case 'C': case 's':
                    // %SC sclk=1Mhz -- SPI configure
                    break;
                case 'D': case 'd':
                    // %SD -- SPI diagnostic messages enable
                    if (g_MAX5719_device.onSPIprint) {
                        g_MAX5719_device.onSPIprint = NULL;
                        // no g_MAX5719_device.loop_limit property; device_has_property(Device, 'loop_limit') != None is false
                    }
                    else {
                        void onSPIprint_handler(size_t byteCount, uint8_t mosiData[], uint8_t misoData[]);
                        g_MAX5719_device.onSPIprint = onSPIprint_handler;
                        // no g_MAX5719_device.loop_limit property; device_has_property(Device, 'loop_limit') is false
                    }
                    break;
                case 'W': case 'R': case 'w': case 'r':
                {
                    // %SW -- write (write and read)
                    // %SR -- read (alias for %SW because SPI always write and read)
                    // parse cmdLine byte list --> int byteCount; int mosiData[MAX_SPI_BYTE_COUNT];
                    #define MAX_SPI_BYTE_COUNT 32
                    size_t byteCount = byteCount;
                    static char mosiData[MAX_SPI_BYTE_COUNT];
                    static char misoData[MAX_SPI_BYTE_COUNT];
                    if (cmdLine.parse_byteCount_byteList_hex(byteCount, mosiData,
                                                             MAX_SPI_BYTE_COUNT))
                    {
                        // hex dump mosiData[0..byteCount-1]
                        cmdLine.serial().printf("\r\nSPI");
                        if (byteCount > 7) {
                            cmdLine.serial().printf(" byteCount:%d", byteCount);
                        }
                        cmdLine.serial().printf(" MOSI->");
                        for (unsigned int byteIndex = 0; byteIndex < byteCount; byteIndex++)
                        {
                            cmdLine.serial().printf(" 0x%2.2X", mosiData[byteIndex]);
                        }
                        spi_cs = 0;
                        unsigned int numBytesTransferred =
                            spi.write(mosiData, byteCount, misoData, byteCount);
                        spi_cs = 1;
                        // hex dump misoData[0..byteCount-1]
                        cmdLine.serial().printf(" MISO<-");
                        for (unsigned int byteIndex = 0; byteIndex < numBytesTransferred;
                             byteIndex++)
                        {
                            cmdLine.serial().printf(" 0x%2.2X", misoData[byteIndex]);
                        }
                        cmdLine.serial().printf(" ");
                    }
                }
                break;
            }             // switch(cmdLine[2])
        }             // case 'S': // %S... -- SPI diagnostics
        break;
#endif
        //
        // A-Z,a-z,0-9 reserved for application use
    }         // switch(cmdLine[1])
} // end void pinsMonitor_submenu_onEOLcommandParser(CmdLine & cmdLine)
#endif // USE_CMDLINE_MENUS support CmdLine command menus

#if USE_CMDLINE_MENUS // support CmdLine command menus
//--------------------------------------------------
void main_menu_onEOLcommandParser(CmdLine& cmdLine)
{
    // process command line
    // TODO: avoid excess console noise when a Button event happens during datalogging -- quiet inside Run_command_table()
    if (g_Run_command_table_running == false) {
        cmdLine.serial().printf("\r\nCmdLine buf:\"%s\"\r\n", cmdLine.str());
    } // if (g_Run_command_table_running)
    // TODO: avoid excess console noise when a Button event happens during datalogging -- quiet inside Run_command_table()
    if (g_Run_command_table_running == false) {
    } // if (g_Run_command_table_running)

#if USE_DATALOGGER_TRIGGER // support Datalog trigger
    // TODO: avoid excess console noise when a Button event happens during datalogging -- quiet inside Run_command_table()
    if (g_Run_command_table_running == false) {
    // If datalog is free running, halt on any possible received command
    if (Datalogger_Trigger != trigger_Halt) {
        Datalogger_Trigger = trigger_Halt;
        cmdLine.serial().printf("Datalog stopped by USB command input\r\n");
        cmdLine.serial().printf("Restart datalog by sending LR\r\n");
    }
    } // if (g_Run_command_table_running)
#endif // USE_DATALOGGER_TRIGGER support Datalog trigger

    // DIAGNOSTIC: print line buffer
    //~ cmdLine.serial().printf("\r\nmain_menu_onEOLcommandParser: ~%s~\r\n", cmdLine.str());
    //
    switch (cmdLine[0])
    {
        case '?':
            main_menu_status(cmdLine);
            main_menu_help(cmdLine);
            // print command prompt
            //cmdLine.serial().printf("\r\n>");
            break;
        case '\r': case '\n':     // ignore blank line
        case '\0':     // ignore empty line
            main_menu_status(cmdLine);
            //~ main_menu_help(cmdLine);
            // print command prompt
            //cmdLine.serial().printf("\r\n>");
            break;
        case '#':     // ignore comment line
            // # -- lines beginning with # are comments
            //
#if USE_DATALOGGER_REMARK_FIELD
            // Datalogger_PrintRow() print gstrRemarkText field from recent #remark -- in main_menu_onEOLcommandParser
            // # command handler update gstrRemarkText buffer instead of just ignoring the remark
            //
            // ignore extra spaces before the remark
            // find argIndex such that cmdLine[argIndex] is the start of the second word
            int argIndex;
            for (argIndex = 1; cmdLine[argIndex] != '\0'; argIndex++)
            {
                if (cmdLine[argIndex] == ' ') break;
            }
            for (; cmdLine[argIndex] != '\0'; argIndex++)
            {
                if (cmdLine[argIndex] != ' ') break;
            }
            //
            strncpy(gstrRemarkText, cmdLine.str()+argIndex, gstrRemarkTextLASTINDEX+1);
            // do not exceed string buffer limit; keep sentinel null character at end of buffer
            gstrRemarkText[gstrRemarkTextLASTINDEX] = '\0';
            for (unsigned int index = 0; index < gstrRemarkTextLASTINDEX; index++)
            {
                if ((gstrRemarkText[index]) == '\0') break; // null character at end of string
                if ((gstrRemarkText[index]) < 0x20) {
                    // replace non-printing characters with _
                    gstrRemarkText[index] = '_';
                    continue;
                }
                if ((gstrRemarkText[index]) >= 0x7F) {
                    // replace non-printing characters with _
                    gstrRemarkText[index] = '_';
                    continue;
                }
                switch(gstrRemarkText[index])
                {
                    case ',':
                        // replace , with ;
                        gstrRemarkText[index] = ';';
                        break;
                    case '"':
                        // replace " with '
                        gstrRemarkText[index] = '\'';
                        break;
                    case '\\':
                        // replace \ with /
                        gstrRemarkText[index] = '/';
                        break;
                }
            }
#endif // USE_DATALOGGER_REMARK_FIELD
            //
    // TODO: avoid excess console noise when a Button event happens during datalogging -- quiet inside Run_command_table()
    if (g_Run_command_table_running == false) {
            main_menu_status(cmdLine);
    } // if (g_Run_command_table_running)
            //~ main_menu_help(cmdLine);
            // print command prompt
            //cmdLine.serial().printf("\r\n>");
            break;
#if ECHO_EOF_ON_EOL
        case '\x04':     // Unicode (U+0004) EOT END OF TRANSMISSION = CTRL+D as EOF end of file
            cmdLine.serial().printf("\x04");     // immediately echo EOF for test scripting
            diagnostic_led_EOF();
            break;
        case '\x1a':     // Unicode (U+001A) SUB SUBSTITUTE = CTRL+Z as EOF end of file
            cmdLine.serial().printf("\x1a");     // immediately echo EOF for test scripting
            diagnostic_led_EOF();
            break;
#endif
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
        case '!':     // device init
            {
                cmdLine.serial().printf("Init");
                // call function Init
                uint8_t result = g_MAX11410_device.Init();
                // cmdLine.serial().printf(" =%d\r\n", result);
                cmdLine.serial().printf(" =%d\r\n", result);
#if USE_CUSTOM_REG_INIT // custom_reg_init_addr[], custom_reg_init_data[], custom_reg_init_count
                // in command '!' device init, apply list of custom register writes after init
                // custom_reg_init_addr[], custom_reg_init_data[], custom_reg_init_count
                for (unsigned int index = 0; index < custom_reg_init_count; index++) {
                    uint8_t regAddress = custom_reg_init_addr[index];
                    uint32_t regData = custom_reg_init_data[index];
                    cmdLine.serial().printf("*%s=0x%06.6x",
                        g_MAX11410_device.RegName((MAX11410::MAX11410_CMD_enum_t)regAddress),
                        regData
                    );
                    g_MAX11410_device.RegWrite((MAX11410::MAX11410_CMD_enum_t)regAddress, regData);
                }
#endif // USE_CUSTOM_REG_INIT
                g_MAX11410_device.v_filter = SPI_AIN_Cfg_v_filter_ch[0];
                g_MAX11410_device.v_ctrl = SPI_AIN_Cfg_v_ctrl_ch[0];
                g_MAX11410_device.v_pga = SPI_AIN_Cfg_v_pga_ch[0];
            }
            break;
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
#if USE_SELFTEST
        case '.':
        {
            // . -- SelfTest
            cmdLine.serial().printf("SelfTest()");
            SelfTest(cmdLine);
        }
        break;
#endif // USE_SELFTEST
#if 1 // APPLICATION_ArduinoPinsMonitor
        case '%':
        {
            pinsMonitor_submenu_onEOLcommandParser(cmdLine);
        }
        break;         // case '%'
#endif // APPLICATION_ArduinoPinsMonitor
#if USE_STAR_REG_READWRITE // * command read/write reg *reg? *reg=value
        // reuse the Serial_Tester command *regName=regValue
// CODE GENERATOR: generate * command read/write reg *reg? *reg=value
        case '*':
        {
            // if buffer starts with a regName:
            // for each reg value (0..n) if(cmdLine.has_keyword(device.regName(r))):
            // cmdLine.serial().printf(" scan RegName...\r\n");
        }
        break;
#endif // USE_STAR_REG_READWRITE // * command read/write reg *reg? *reg=value
//
#if 1 // USE_AUX_SERIAL_CMD_FORWARDING && (HAS_AUX_SERIAL || HAS_DAPLINK_SERIAL)
        // TODO WIP Command forwarding to Auxiliary serial port TX/RX #257 -- main_menu_onEOLcommandParser
        case '>': case '<': // > send and receive; < receive without sending anything
        {
            // prefer cmdLine_AUXserial if available, else cmdLine_DAPLINKserial; else we don't have this feature
# if HAS_AUX_SERIAL
            // TODO WIP Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257
            CmdLine& cmdLine_AuxSerial = cmdLine_AUXserial;
            Serial& AuxSerial = AUXserial;
# elif HAS_DAPLINK_SERIAL
            // TODO WIP Command forwarding to DAPLINK serial TX/RX cmdLine_DAPLINKserial #257
            CmdLine& cmdLine_AuxSerial = cmdLine_DAPLINKserial;
            Serial& AuxSerial = DAPLINKserial;
# else // neither HAS_AUX_SERIAL HAS_DAPLINK_SERIAL
#warning "USE_AUX_SERIAL_CMD_FORWARDING should not be enabled without HAS_AUX_SERIAL or HAS_DAPLINK_SERIAL"
# endif // HAS_AUX_SERIAL HAS_DAPLINK_SERIAL
            //
            int g_auxSerialCom_outgoing_string_cr = 1; // option to send CR after outgoing_string
            int g_auxSerialCom_outgoing_string_lf = 0; // option to send LF after outgoing_string (breaks remote datalogger LR!)
            int g_auxSerialCom_rx_string_verbose_crlf = 0; // option to display \r\n as "\\r\\n" in rx_string_buf
            int g_auxSerialCom_rx_string_verbose_ctrl = 1; // option to display control codes as \xXX
            const char* g_auxSerialCom_display_tx_prefix = "\r\n->|"; // "\r\n >";
            const char* g_auxSerialCom_display_rx_prefix = "\r\n<-|"; // "\r\n< ";
            //
            bool need_g_auxSerialCom_display_rx_prefix = 1; // temp
            //
            // >> suppress key=value parsing
            bool suppress_parsing = (cmdLine[1] == '>');
            if (suppress_parsing == false) {
                // int g_auxSerialCom_baud = 9600; //!< baud rate Auxiliary serial port
                if (cmdLine.parse_int_dec("baud", g_auxSerialCom_baud))
                {
                    // Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257 -- baud rate
                    cmdLine_AuxSerial.serial().printf("\r\n*** New Baud Rate %d ***\r\n", g_auxSerialCom_baud);
                    AuxSerial.baud(g_auxSerialCom_baud);
                    cmdLine_AuxSerial.serial().printf("\r\n*** Baud Rate was set to %d ***\r\n", g_auxSerialCom_baud);
                }
#if 0
                // int g_auxSerialCom_tx_wait_echo = 0;       //!< TX wait for each character echo?
                if (cmdLine.parse_int_dec("tx_wait_echo", g_auxSerialCom_tx_wait_echo))
                {
                    // Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257 -- tx_wait_echo
                    //~ cmdLine_AuxSerial.serial().printf("\r\n*** tx_wait_echo was set to %d ***\r\n", g_auxSerialCom_tx_wait_echo);
                    cmdLine.serial().printf("\r\n  tx_wait_echo=%d", g_auxSerialCom_tx_wait_echo);
                }
                // int g_auxSerialCom_tx_char_delay_ms = 0;   //!< TX delay after each char?
                if (cmdLine.parse_int_dec("tx_char_delay_ms", g_auxSerialCom_tx_char_delay_ms))
                {
                    // Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257 -- tx_char_delay_ms
                    //~ cmdLine_AuxSerial.serial().printf("\r\n*** tx_char_delay_ms was set to %d ***\r\n", g_auxSerialCom_tx_char_delay_ms);
                    cmdLine.serial().printf("\r\n  tx_char_delay_ms=%dms", g_auxSerialCom_tx_char_delay_ms);
                }
                // int g_auxSerialCom_tx_line_delay_ms = 0;   //!< TX delay after each CR/LF?
                if (cmdLine.parse_int_dec("tx_line_delay_ms", g_auxSerialCom_tx_line_delay_ms))
                {
                    // Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257 -- tx_line_delay_ms
                    //~ cmdLine_AuxSerial.serial().printf("\r\n*** tx_line_delay_ms was set to %d ***\r\n", g_auxSerialCom_tx_line_delay_ms);
                    cmdLine.serial().printf("\r\n  tx_line_delay_ms=%dms", g_auxSerialCom_tx_line_delay_ms);
                }
#endif
                // int g_auxSerialCom_message_ms = 0;     //!< capture RX until response timeout?
                if (cmdLine.parse_int_dec("message_ms", g_auxSerialCom_message_ms))
                {
                    // Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257 -- message_ms
                    //~ cmdLine_AuxSerial.serial().printf("\r\n*** message_ms was set to %d ***\r\n", g_auxSerialCom_message_ms);
                    cmdLine.serial().printf("\r\n  message_ms timeout %dms", g_auxSerialCom_message_ms);
                }
                // int g_auxSerialCom_rx_idle_ms = 0;     //!< capture RX until idle timeout?
                if (cmdLine.parse_int_dec("rx_idle_ms", g_auxSerialCom_rx_idle_ms))
                {
                    // Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257 -- rx_idle_ms
                    //~ cmdLine_AuxSerial.serial().printf("\r\n*** rx_idle_ms was set to %d ***\r\n", g_auxSerialCom_rx_idle_ms);
                    cmdLine.serial().printf("\r\n  rx_idle_ms timeout %dms", g_auxSerialCom_rx_idle_ms);
                }
                // int g_auxSerialCom_rx_max_count = 0;   //!< capture RX until max character count?
                if (cmdLine.parse_int_dec("rx_max_count", g_auxSerialCom_rx_max_count))
                {
                    // Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257 -- rx_max_count
                    //~ cmdLine_AuxSerial.serial().printf("\r\n*** rx_max_count was set to %d ***\r\n", g_auxSerialCom_rx_max_count);
                    cmdLine.serial().printf("\r\n  rx_max_count %d bytes", g_auxSerialCom_rx_max_count);
                }
                // int g_auxSerialCom_rx_eot = 0;         //!< capture RX until match end of text char?
                if (cmdLine.parse_int_dec("rx_eot", g_auxSerialCom_rx_eot))
                {
                    // Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257 -- rx_eot
                    //~ cmdLine_AUXserial.serial().printf("\r\n*** rx_eot was set to %d ***\r\n", g_auxSerialCom_rx_eot);
                    cmdLine.serial().printf("\r\n  rx_eot %d", g_auxSerialCom_rx_eot);
                }
            }
            // Command forwarding to AUX serial TX/RX cmdLine_AuxSerial #257 -- send outgoing_string
            char* outgoing_string = ""; // warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
            if (cmdLine[0] == '>') {
                outgoing_string = (char*)cmdLine.str();
                // > use key=value parsing
                // >> suppress key=value parsing
                if (suppress_parsing) {
                    cmdLine.serial().printf("\r\n  suppress_parsing outgoing_string=\"%s\"", outgoing_string);
                    outgoing_string++; // skip the first '>'
                    outgoing_string++; // skip the second '>'
                } else {
                    // TODO: after parsing, key=value pairs should be deleted, but outgoing_string=">xyzzy abc=def"
                    cmdLine.serial().printf("\r\n  after parsing, outgoing_string=\"%s\"", outgoing_string);
                    outgoing_string++; // skip the first '>'
                }
            } // if (cmdLine[0] == '>')
            static char rx_string_buf[RX_STRING_BUF_SIZE];
            unsigned int rx_string_length = 0;
            //cmdLine.serial().printf("\r\n  >%s\r\n  <", outgoing_string);
            //char* g_auxSerialCom_display_tx_prefix = "\r\n >";
            //char* g_auxSerialCom_display_rx_prefix = "\r\n< ";
            cmdLine.serial().printf("%s%s%s", 
                    g_auxSerialCom_display_tx_prefix, // "\r\n  >"
                    outgoing_string,
                    g_auxSerialCom_display_rx_prefix  // "\r\n  <"
                );
            rx_string_buf[0] = '\0';
            rx_string_length = 0;
            //
            // int g_auxSerialCom_tx_wait_echo = 0;       //!< TX wait for each character echo?
            // int g_auxSerialCom_tx_char_delay_ms = 0;   //!< TX delay after each char?
            // int g_auxSerialCom_tx_line_delay_ms = 0;   //!< TX delay after each CR/LF?
            //
            // int g_auxSerialCom_Timer_begin_message_ms = 0; //!< start of message
            // int g_auxSerialCom_Timer_begin_rx_idle_ms = 0; //!< recent RX character timestamp
            // int g_auxSerialCom_message_ms = 10000;     //!< maximum RX message total response time
            // int g_auxSerialCom_rx_idle_ms = 2000;     //!< maximum RX message idle time between characters
            // int g_auxSerialCom_rx_max_count = RX_STRING_BUF_SIZE-1;   //!< maximum RX message total length
            // int g_auxSerialCom_rx_eot = '\r';         //!< capture RX until match end of text char
            //~ cmdLine_AuxSerial.serial().printf("\r\n*** TODO forward %s ***\r\n", outgoing_string);
            //
            // TODO: send whole string or send character-by-character?
            if (cmdLine[0] == '>') {
                cmdLine_AuxSerial.serial().printf("%s", outgoing_string);
                //
                // TODO: send CRLF or just send CR line end?
                if (g_auxSerialCom_outgoing_string_cr) { cmdLine_AuxSerial.serial().printf("\r"); }
                if (g_auxSerialCom_outgoing_string_lf) { cmdLine_AuxSerial.serial().printf("\n"); }
                // cmdLine_AuxSerial.serial().printf("\r\n");
            } // if (cmdLine[0] == '>')
            //
            g_auxSerialCom_Timer.start();
            g_auxSerialCom_Timer_begin_message_ms = g_auxSerialCom_Timer.read_ms(); // start of message
            g_auxSerialCom_Timer_begin_rx_idle_ms = g_auxSerialCom_Timer.read_ms(); // recent RX character timestamp
            while (rx_string_length < (RX_STRING_BUF_SIZE-1)) {
                if ((g_auxSerialCom_Timer.read_ms() - g_auxSerialCom_Timer_begin_message_ms) > g_auxSerialCom_message_ms) {
                    cmdLine.serial().printf("\r\n  message_ms timeout %dms", g_auxSerialCom_message_ms);
                    break;
                }
                if ((g_auxSerialCom_Timer.read_ms() - g_auxSerialCom_Timer_begin_rx_idle_ms) > g_auxSerialCom_rx_idle_ms) {
                    cmdLine.serial().printf("\r\n  rx_idle_ms timeout %dms", g_auxSerialCom_rx_idle_ms);
                    break;
                }
                if ((int)rx_string_length >= g_auxSerialCom_rx_max_count) {
                    cmdLine.serial().printf("\r\n  rx_max_count %d bytes", g_auxSerialCom_rx_max_count);
                    break;
                }
                if (AuxSerial.readable()) {
                    g_auxSerialCom_Timer_begin_rx_idle_ms = g_auxSerialCom_Timer.read_ms(); // recent RX character timestamp
                    char ch = AuxSerial.getc();
                    rx_string_buf[rx_string_length++] = ch;
                    rx_string_buf[rx_string_length] = '\0'; // null terminate buffer
                    //
                    #if 1
                    // immediate character echo
                    for (char* cp = &(rx_string_buf[rx_string_length-1]); *cp != '\0'; cp++)
                    {
                        //cmdLine.serial().printf("\r\n  <%s\r\n", rx_string_buf);
                        if (need_g_auxSerialCom_display_rx_prefix) {
                            cmdLine.serial().printf(g_auxSerialCom_display_rx_prefix); // "\r\n< " g_auxSerialCom_rx_string display prefix
                            need_g_auxSerialCom_display_rx_prefix = 0;
                        }
                        if ((*cp) == '\r') {
                            if (g_auxSerialCom_rx_string_verbose_crlf) { cmdLine.serial().printf("\\r"); }
                            // if (*(cp+1) != '\n') { need_g_auxSerialCom_display_rx_prefix = 1; }
                        } else if ((*cp) == '\n') {
                            if (g_auxSerialCom_rx_string_verbose_crlf) { cmdLine.serial().printf("\\n"); }
                            need_g_auxSerialCom_display_rx_prefix = 1;
                            if (*(cp-1) != '\r') { need_g_auxSerialCom_display_rx_prefix = 1; }
                        } else if ((*cp) < 0x20) {
                            if (g_auxSerialCom_rx_string_verbose_ctrl) { cmdLine.serial().printf("\\x%2.2x", *cp); }
                        } else if ((*cp) >= 0x7f) {
                            if (g_auxSerialCom_rx_string_verbose_ctrl) { cmdLine.serial().printf("\\x%2.2x", *cp); }
                        } else {
                            cmdLine.serial().printf("%c", *cp);
                        }
                    }
                    #else
                    // immediate character echo
                    cmdLine.serial().printf("%s", &(rx_string_buf[rx_string_length-1]) );
                    #endif
                    //
                    if (g_auxSerialCom_rx_eot != aux_serial_cmd_forwarding_rx_eot_not_used) {
                        if (ch == g_auxSerialCom_rx_eot) {
                            cmdLine.serial().printf("\r\n  rx_eot %d", g_auxSerialCom_rx_eot);
                            break;
                        }
                    }
                }
            } // end while (rx_string_length < (RX_STRING_BUF_SIZE-1))
            #if 0
            // print summary. is this needed? we already print aux rx as it is received.
            rx_string_buf[rx_string_length] = '\0'; // null terminate buffer
            # if 1 // support multi-line rx_string_buf response 
            if (cmdLine[0] == '>') {
                cmdLine.serial().printf("%s%s", g_auxSerialCom_display_tx_prefix, outgoing_string);
            } // if (cmdLine[0] == '>')
            // cmdLine.serial().printf("\r\n  <"); // prefix
            need_g_auxSerialCom_display_rx_prefix = 1;
            for (char* cp = rx_string_buf; *cp != '\0'; cp++)
            {
                //cmdLine.serial().printf("\r\n  <%s\r\n", rx_string_buf);
                if (need_g_auxSerialCom_display_rx_prefix) {
                    cmdLine.serial().printf(g_auxSerialCom_display_rx_prefix); // "\r\n< " g_auxSerialCom_rx_string display prefix
                    need_g_auxSerialCom_display_rx_prefix = 0;
                }
                if ((*cp) == '\r') {
                    if (g_auxSerialCom_rx_string_verbose_crlf) { cmdLine.serial().printf("\\r"); }
                    if (*(cp+1) != '\n') { need_g_auxSerialCom_display_rx_prefix = 1; }
                } else if ((*cp) == '\n') {
                    if (g_auxSerialCom_rx_string_verbose_crlf) { cmdLine.serial().printf("\\n"); }
                    need_g_auxSerialCom_display_rx_prefix = 1;
                } else if ((*cp) < 0x20) {
                    if (g_auxSerialCom_rx_string_verbose_ctrl) { cmdLine.serial().printf("\\x%2.2x", *cp); }
                } else if ((*cp) >= 0x7f) {
                    if (g_auxSerialCom_rx_string_verbose_ctrl) { cmdLine.serial().printf("\\x%2.2x", *cp); }
                } else {
                    cmdLine.serial().printf("%c", *cp);
                }
            }
            cmdLine.serial().printf("\r\n");
            # else
            cmdLine.serial().printf("\r\n  >%s", outgoing_string);
            cmdLine.serial().printf("\r\n  <%s\r\n", rx_string_buf);
            # endif
            #endif
            //
        }
        break;         // case '>'
#endif // USE_AUX_SERIAL_CMD_FORWARDING
//
#if USE_DATALOGGER_TRIGGER // support Datalog trigger
        // TODO Datalog trigger menu
        // set Datalogger_Trigger to trigger_Halt or trigger_FreeRun
        // Datalogger_Trigger = trigger_Halt // halt the datalogger; continue accepting commands
        // Datalogger_Trigger = trigger_FreeRun // free run as fast as possible
        case 'L': case 'l':
        {
            // halt the datalogger; continue accepting commands
            Datalogger_Trigger = trigger_Halt;
            switch (cmdLine[1])
            {
                // LT%1.0fs -- Datalog timer sleep=%1.3f seconds g_timer_interval_msec
                case 'T': case 't':
                    {
                    // timer (configure interval) "intermittent-sleep-mode"
                    Datalogger_Trigger = trigger_Timer;
                    Datalogger_Need_PrintHeader = true;
                    g_timer_interval_counter = 0;
                    // get g_timer_interval_msec from cmdLine
                    // g_timer_interval_msec = 500;
                    // g_timer_interval_count = 10;
                    if (cmdLine.parse_int_dec("count", g_timer_interval_count))
                    {
                        if (g_timer_interval_count < 1) { g_timer_interval_count = 1; }
                        // Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257 -- message_ms
                        //~ cmdLine_AuxSerial.serial().printf("\r\n*** message_ms was set to %d ***\r\n", g_auxSerialCom_message_ms);
                        cmdLine.serial().printf("\r\n  g_timer_interval_count=%d", g_timer_interval_count);
                    }
                    if (cmdLine.parse_int_dec("base", g_timer_interval_msec))
                    {
                        // Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257 -- message_ms
                        //~ cmdLine_AuxSerial.serial().printf("\r\n*** message_ms was set to %d ***\r\n", g_auxSerialCom_message_ms);
                        cmdLine.serial().printf("\r\n  g_timer_interval_msec=%d", g_timer_interval_msec);
                    }
#if USE_DATALOGGER_SLEEP // support sleep modes in Datalogger_Trigger
                    // TODO: USE_DATALOGGER_SLEEP -- support sleep modes in Datalogger_Trigger
                    int int_timer_sleepmode = g_timer_sleepmode;
                    if (cmdLine.parse_int_dec("sleep", int_timer_sleepmode))
                    {
                        g_timer_sleepmode = (Datalogger_Sleep_enum_t)int_timer_sleepmode;
                        //~ if (g_timer_interval_count < 1) { g_timer_interval_count = 1; }
                        // Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257 -- message_ms
                        //~ cmdLine_AuxSerial.serial().printf("\r\n*** message_ms was set to %d ***\r\n", g_auxSerialCom_message_ms);
                        cmdLine.serial().printf("\r\n  g_timer_sleepmode=%d", (int)g_timer_sleepmode);
                    }
#else // USE_DATALOGGER_SLEEP
#endif // USE_DATALOGGER_SLEEP
                    return; // instead of break; avoid falling through to print_command_prompt();
                    }
                    break;
                // LIH3 -- Datalog when input high D3
                // LIL6 -- Datalog when input low D6
                case 'I': case 'i':
                    {
                        // TODO: switch cmdLine[2] configure High or Low, configure input pin
                    }
                    break;
#if USE_DATALOGGER_SPIDeviceRegRead
                // L$ count=10 base=500ms addr=xxx data=xxx mask=xxx -- Datalog when SPI read of address matches mask
                case '$':
                    {
                        // TODO: scan cmdLine for regAddr, dataMask, testValue
                    }
                    break;
#endif // USE_DATALOGGER_SPIDeviceRegRead
#if USE_DATALOGGER_ActionTable // Datalogger_RunActionTable() supported actions
                // L@ -- configures Datalogger_action_table
                case '@':
                    {
                        // L@ -- configures Datalogger_action_table
                        int editRowIndex = 0;
                        int edit_action = (int)action_noop;
                        int edit_digitalOutPin = 0;
                        int edit_condition = (int)condition_always;
                        int edit_condition_channel = 0;
                        double edit_condition_threshold = 0;
                        //
                        if (cmdLine.parse_int_dec("act", edit_action))
                        {
                            //~ cmdLine.serial().printf("\r\n  act=%d", edit_action);
                        }
                        if (cmdLine.parse_int_dec("pin", edit_digitalOutPin))
                        {
                            //~ cmdLine.serial().printf("\r\n  pin=%d", edit_digitalOutPin);
                        }
                        if (cmdLine.parse_int_dec("if", edit_condition))
                        {
                            //~ cmdLine.serial().printf("\r\n  if=%d", edit_condition);
                        }
                        if (cmdLine.parse_int_dec("ch", edit_condition_channel))
                        {
                            //~ cmdLine.serial().printf("\r\n  ch=%d", edit_condition_channel);
                        }
                        if (cmdLine.parse_double("x", edit_condition_threshold))
                        {
                            //~ cmdLine.serial().printf("\r\n  x=%d", edit_condition_threshold);
                        }
                        //
                        // Edit the contents of Datalogger_action_table
                        switch (cmdLine[2]) // L@... -- Edit the contents of Datalogger_action_table
                        {
                            // LT%1.0fs -- Datalog timer sleep=%1.3f seconds g_timer_interval_msec
                            case '0': case '1': case '2': case '3': case '4':
                            case '5': case '6': case '7': case '8': case '9':
                                // edit row data
                                if (1) { // removed Datalogger_action_table_row_count > 0 -- support %L@nnn command add new row (even though this looks like a replace command) if and only if nnn==next new unassigned line number
                                    // if Datalogger_action_table_row_count == 0 then the table is empty
                                    // get row number to edit from cmdLine[2]
                                    editRowIndex = atoi(cmdLine.str()+2);
                                    if ((editRowIndex >= 0) && (editRowIndex < Datalogger_action_table_row_count)) {
                                        // update row
                                        cmdLine.serial().printf("\r\n replace row %d", editRowIndex);
                                        // rescan cmdLine[2] for key=value of what fields to change (now that we have got defaults from the existing line
                                        edit_action = Datalogger_action_table[editRowIndex].action;
                                        edit_digitalOutPin = Datalogger_action_table[editRowIndex].digitalOutPin;
                                        edit_condition = Datalogger_action_table[editRowIndex].condition;
                                        edit_condition_channel = Datalogger_action_table[editRowIndex].condition_channel;
                                        edit_condition_threshold = Datalogger_action_table[editRowIndex].condition_threshold;
                                        // rescan cmdLine[2] for key=value of what fields to change (now that we have got defaults from the existing line
                                        if (cmdLine.parse_int_dec("act", edit_action))
                                        {
                                            //~ cmdLine.serial().printf("\r\n  act=%d", edit_action);
                                        }
                                        if (cmdLine.parse_int_dec("pin", edit_digitalOutPin))
                                        {
                                            //~ cmdLine.serial().printf("\r\n  pin=%d", edit_digitalOutPin);
                                        }
                                        if (cmdLine.parse_int_dec("if", edit_condition))
                                        {
                                            //~ cmdLine.serial().printf("\r\n  if=%d", edit_condition);
                                        }
                                        if (cmdLine.parse_int_dec("ch", edit_condition_channel))
                                        {
                                            //~ cmdLine.serial().printf("\r\n  ch=%d", edit_condition_channel);
                                        }
                                        if (cmdLine.parse_double("x", edit_condition_threshold))
                                        {
                                            //~ cmdLine.serial().printf("\r\n  x=%d", edit_condition_threshold);
                                        }
                                        Datalogger_action_table[editRowIndex].action = (action_type_enum_t)edit_action;
                                        Datalogger_action_table[editRowIndex].digitalOutPin = edit_digitalOutPin;
                                        Datalogger_action_table[editRowIndex].condition = (action_condition_enum_t)edit_condition;
                                        Datalogger_action_table[editRowIndex].condition_channel = edit_condition_channel;
                                        Datalogger_action_table[editRowIndex].condition_threshold = edit_condition_threshold;
                                    }
                                    else if ((editRowIndex == Datalogger_action_table_row_count) && (Datalogger_action_table_row_count < ACTION_TABLE_ROW_MAX)) {
                                        // %L@nnn command add new row (even though this looks like a replace command) if and only if nnn==next new unassigned line number
                                        Datalogger_action_table_row_count++;
                                        cmdLine.serial().printf("\r\n add next row %d", editRowIndex);
                                        Datalogger_action_table[editRowIndex].action = (action_type_enum_t)edit_action;
                                        Datalogger_action_table[editRowIndex].digitalOutPin = edit_digitalOutPin;
                                        Datalogger_action_table[editRowIndex].condition = (action_condition_enum_t)edit_condition;
                                        Datalogger_action_table[editRowIndex].condition_channel = edit_condition_channel;
                                        Datalogger_action_table[editRowIndex].condition_threshold = edit_condition_threshold;
                                        //
                                    }
                                }
                                break;
                            case '+':
                                // add a new row at end of table
                                if (Datalogger_action_table_row_count < ACTION_TABLE_ROW_MAX) {
                                    // if Datalogger_action_table_row_count => ACTION_TABLE_ROW_MAX then the table is full
                                    editRowIndex = Datalogger_action_table_row_count;
                                    Datalogger_action_table_row_count++;
                                    cmdLine.serial().printf("\r\n add new row %d", editRowIndex);
                                    Datalogger_action_table[editRowIndex].action = (action_type_enum_t)edit_action;
                                    Datalogger_action_table[editRowIndex].digitalOutPin = edit_digitalOutPin;
                                    Datalogger_action_table[editRowIndex].condition = (action_condition_enum_t)edit_condition;
                                    Datalogger_action_table[editRowIndex].condition_channel = edit_condition_channel;
                                    Datalogger_action_table[editRowIndex].condition_threshold = edit_condition_threshold;
                                }
                                break;
                            case '-':
                                // delete row from table
                                if (Datalogger_action_table_row_count > 0) {
                                    // if Datalogger_action_table_row_count == 0 then the table is empty
                                    if ((cmdLine[3] == '~') && (cmdLine[4] == '~') && (cmdLine[5] == '~')) {
                                        // L@-~~~ -- delete all rows from table
                                        cmdLine.serial().printf("\r\n delete all rows");
                                        Datalogger_action_table_row_count = 0;
                                        break;
                                    }
                                    else {
                                        editRowIndex = atoi(cmdLine.str()+3);
                                        cmdLine.serial().printf("\r\n delete row %d", editRowIndex);
                                        // delete row editRowIndex from Datalogger_action_table
                                        for (int i = editRowIndex; i < (Datalogger_action_table_row_count-1); i++)
                                        {
                                            // copy row i+1 into row i
                                            Datalogger_action_table[i].action = Datalogger_action_table[i+1].action;
                                            Datalogger_action_table[i].digitalOutPin = Datalogger_action_table[i+1].digitalOutPin;
                                            Datalogger_action_table[i].condition = Datalogger_action_table[i+1].condition;
                                            Datalogger_action_table[i].condition_channel = Datalogger_action_table[i+1].condition_channel;
                                            Datalogger_action_table[i].condition_threshold = Datalogger_action_table[i+1].condition_threshold;
                                        }
                                        Datalogger_action_table_row_count--;
                                    }
                                }
                                break;
                            case '.':
                                // L@. pause the entire Log action table
                                cmdLine.serial().printf("\r\n pause the entire Log action table");
                                Datalogger_action_table_enabled = false;
                                break;
                            case '@':
                                // L@@ enable the entire Log action table
                                cmdLine.serial().printf("\r\n enable the entire Log action table");
                                Datalogger_action_table_enabled = true;
                                break;
                        }
                        //
                        // Print the contents of Datalogger_action_table
                        if (Datalogger_action_table_enabled) {
                            // cmdLine.serial().printf("Log action table enabled; L@. to pause");
                        }
                        else {
                            cmdLine.serial().printf("Log action table paused; L@@ to enable");
                        }
                        //~ cmdLine.serial().printf("\r\n  Datalogger_action_table_row_count=%d", Datalogger_action_table_row_count);
                        for (int i = 0; i < Datalogger_action_table_row_count; i++)
                        {
                            cmdLine.serial().printf("\r\n L@%d", i);
                            cmdLine.serial().printf(" act=%d", Datalogger_action_table[i].action);
                            switch(Datalogger_action_table[i].action)
                            {
                                case action_digitalOutLow:
                                case action_digitalOutHigh:
                                    cmdLine.serial().printf(" pin=%d", Datalogger_action_table[i].digitalOutPin);
                                    break;
                                case action_button:  // pin = button index 1, 2, 3
                                    cmdLine.serial().printf(" pin=%d", Datalogger_action_table[i].digitalOutPin);
                                    break;
                                default:
                                case action_noop:
                                case action_trigger_Halt:
                                case action_trigger_FreeRun:
                                case action_trigger_Timer:
                                case action_trigger_PlatformDigitalInput:
                                case action_trigger_SPIDeviceRegRead:
                                    break;
                            }
                            switch(Datalogger_action_table[i].condition)
                            {
                                case condition_always:
                                    break;
                                default:
                                case condition_if_An_gt_threshold:
                                case condition_if_An_lt_threshold:
                                case condition_if_An_eq_threshold:
                                case condition_if_An_ge_threshold:
                                case condition_if_An_le_threshold:
                                case condition_if_An_ne_threshold:
                                case condition_if_AINn_gt_threshold:
                                case condition_if_AINn_lt_threshold:
                                case condition_if_AINn_eq_threshold:
                                case condition_if_AINn_ge_threshold:
                                case condition_if_AINn_le_threshold:
                                case condition_if_AINn_ne_threshold:
                                    cmdLine.serial().printf(" if=%d", Datalogger_action_table[i].condition);
                                    cmdLine.serial().printf(" ch=%d", Datalogger_action_table[i].condition_channel);
                                    // our voltage measurement is around 1mV at best
                                    cmdLine.serial().printf(" x=%1.4f", Datalogger_action_table[i].condition_threshold);
                                    break;
                            }
                            cmdLine.serial().printf(" -- ");
                            switch(Datalogger_action_table[i].action)
                            {
                                case action_noop:
                                    cmdLine.serial().printf("No_Operation");
                                    break;
                                case action_digitalOutLow:
                                    cmdLine.serial().printf("digitalOutLow");
                                    cmdLine.serial().printf(" D%d", Datalogger_action_table[i].digitalOutPin);
                                    break;
                                case action_digitalOutHigh:
                                    cmdLine.serial().printf("digitalOutHigh");
                                    cmdLine.serial().printf(" D%d", Datalogger_action_table[i].digitalOutPin);
                                    break;
                                case action_button:  // pin = button index 1, 2, 3
                                    cmdLine.serial().printf("button %%B%d!", Datalogger_action_table[i].digitalOutPin);
                                    break;
                                case action_trigger_Halt:
                                    cmdLine.serial().printf("Halt");
                                    break;
                                case action_trigger_FreeRun:
                                    cmdLine.serial().printf("LR");
                                    break;
                                case action_trigger_Timer:
                                    cmdLine.serial().printf("LT count=%d base=%dms sleep=%d", g_timer_interval_count, g_timer_interval_msec, g_timer_sleepmode);
                                    break;
                                case action_trigger_PlatformDigitalInput:
                                    cmdLine.serial().printf("trigger_PlatformDigitalInput");
                                    // TODO: print configuration for trigger_PlatformDigitalInput
                                    //~ cmdLine.serial().printf("(%d)", g_config_PlatformDigitalInput);
                                    break;
                                case action_trigger_SPIDeviceRegRead:
                                    cmdLine.serial().printf("trigger_SPIDeviceRegRead");
                                    // TODO: print configuration for trigger_SPIDeviceRegRead
                                    //~ cmdLine.serial().printf("(%d)", g_config_SPIDeviceRegRead);
                                    break;
                            } // switch(Datalogger_action_table[i].action)
                            //~ cmdLine.serial().printf(" condition %d", Datalogger_action_table[i].condition);
                            switch(Datalogger_action_table[i].condition)
                            {
                                case condition_always:
                                    cmdLine.serial().printf(" always");
                                    break;
                                case condition_if_An_gt_threshold:
                                    cmdLine.serial().printf(" if A%d > %1.2f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
                                    break;
                                case condition_if_An_lt_threshold:
                                    cmdLine.serial().printf(" if A%d < %1.2f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
                                    break;
                                case condition_if_An_eq_threshold:
                                    cmdLine.serial().printf(" if A%d == %1.2f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
                                    break;
                                case condition_if_An_ge_threshold:
                                    cmdLine.serial().printf(" if A%d >= %1.2f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
                                    break;
                                case condition_if_An_le_threshold:
                                    cmdLine.serial().printf(" if A%d <= %1.2f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
                                    break;
                                case condition_if_An_ne_threshold:
                                    cmdLine.serial().printf(" if A%d != %1.2f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
                                    break;
                                case condition_if_AINn_gt_threshold:
                                    cmdLine.serial().printf(" if AIN%d > %1.2f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
                                    break;
                                case condition_if_AINn_lt_threshold:
                                    cmdLine.serial().printf(" if AIN%d < %1.2f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
                                    break;
                                case condition_if_AINn_eq_threshold:
                                    cmdLine.serial().printf(" if AIN%d == %1.2f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
                                    break;
                                case condition_if_AINn_ge_threshold:
                                    cmdLine.serial().printf(" if AIN%d >= %1.2f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
                                    break;
                                case condition_if_AINn_le_threshold:
                                    cmdLine.serial().printf(" if AIN%d <= %1.2f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
                                    break;
                                case condition_if_AINn_ne_threshold:
                                    cmdLine.serial().printf(" if AIN%d != %1.2f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
                                    break;
                            } // switch(Datalogger_action_table[i].condition)
                        } // for ...Datalogger_action_table_row_count // Print the contents of Datalogger_action_table
                        cmdLine.serial().printf("\r\n\r\nEdit Log action table (used %d/%d, %d free):",
                            Datalogger_action_table_row_count,
                            ACTION_TABLE_ROW_MAX,
                            (ACTION_TABLE_ROW_MAX - Datalogger_action_table_row_count)
                            );
                        if (Datalogger_action_table_row_count < ACTION_TABLE_ROW_MAX) {
                            // if Datalogger_action_table_row_count => ACTION_TABLE_ROW_MAX then the table is full
                            cmdLine.serial().printf("\r\n L@+ act=4 if=1 ch=5 x=12345 -- add new entry at end of table");
                        }
                        if (Datalogger_action_table_row_count > 0) {
                            // if Datalogger_action_table_row_count == 0 then the table is empty
                            cmdLine.serial().printf("\r\n L@%d act=4 if=1 ch=5 x=12345 -- edit row %d", 
                                    Datalogger_action_table_row_count-1,
                                    Datalogger_action_table_row_count-1);
                            cmdLine.serial().printf("\r\n L@-%d -- delete row %d", 
                                Datalogger_action_table_row_count-1,
                                Datalogger_action_table_row_count-1);
                            cmdLine.serial().printf("\r\n L@-~~~ -- delete all rows");
                            if (Datalogger_action_table_enabled) cmdLine.serial().printf("\r\n L@. -- pause entire Log action table");
                            if (!Datalogger_action_table_enabled) cmdLine.serial().printf("\r\n L@@ -- enable Log action table");
                        }
                        //
                    } // case L@ -- configures Datalogger_action_table
                    break;
#endif // USE_DATALOGGER_ActionTable Datalogger_RunActionTable() supported actions
                // LR -- Datalog free run as fast as possible
                case 'R': case 'r':
                    // free run as fast as possible
                    Datalogger_Trigger = trigger_FreeRun;
                    Datalogger_Need_PrintHeader = true;
                    return; // instead of break; avoid falling through to print_command_prompt();
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
                // LS<channel ID><verb>: Configure SPI_AIN channels
                // channel ID: 0,1,2,... or - for all channels
                // verb: D for disable, V for Voltage, L for LSB
                case 'S': case 's':
                    {
                        for (int channel_index = 0; channel_index < NUM_DUT_ANALOG_IN_CHANNELS; channel_index++) {
                            if ((cmdLine[2] == '-' /* all channels */) 
                            ||  (cmdLine[2] == '0'+channel_index)
                               )
                            {
                                // it's me
                                // test cmdLine[3] to determine action
                                // Platform_Enable_ch[channel_index] = Platform_AIN_xxxxx;
                                switch (cmdLine[3])
                                {
                                    case 'D': case 'd':
                                        SPI_AIN_Enable_ch[channel_index] = SPI_AIN_Disable;
                                        break;
                                    case 'L': case 'l':
                                        SPI_AIN_Enable_ch[channel_index] = SPI_AIN_Enable_LSB;
                                        break;
                                    case 'V': case 'v':
                                        SPI_AIN_Enable_ch[channel_index] = SPI_AIN_Enable_Volt;
                                        break;
                                    //
                                    //
                                    // TODO: MAX11410 verb for configuring SPI_AIN_Cfg_v_ctrl_ch[channel_index]
                                    case 'C': case 'c':
                                    {
                                        uint8_t hexValue = 0;
                                        hexValue = hexValueOfChar(cmdLine[5]) * 0x10 + hexValueOfChar(cmdLine[6]);
                                        switch (cmdLine[4])
                                        {
                                            //
                                            // MAX11410 verb for configuring SPI_AIN_Cfg_v_filter_ch[channel_index]
                                            // cmdLine.serial().print(F("\r\n LS-CF34 -- Datalog SPI ADC channel '-'(all) v_filter 0x34"));
                                            case 'F': case 'f':
                                                SPI_AIN_Cfg_v_filter_ch[channel_index] = hexValue;
                                                break;
                                            //
                                            // MAX11410 verb for configuring SPI_AIN_Cfg_v_ctrl_ch[channel_index]
                                            // cmdLine.serial().print(F("\r\n LS-CC42 -- Datalog SPI ADC channel '-'(all) v_ctrl 0x42"));
                                            case 'C': case 'c':
                                                SPI_AIN_Cfg_v_ctrl_ch[channel_index] = hexValue;
                                                break;
                                            //
                                            // MAX11410 verb for configuring SPI_AIN_Cfg_v_pga_ch[channel_index]
                                            // cmdLine.serial().print(F("\r\n LS-CP00 -- Datalog SPI ADC channel '-'(all) v_pga 0x00"));
                                            case 'P': case 'p':
                                                SPI_AIN_Cfg_v_pga_ch[channel_index] = hexValue;
                                                break;
                                            //
                                            // cmdLine.serial().print(F("\r\n LS5CU -- Datalog SPI ADC channel channel 5 v_ctrl Unipolar"));
                                            // ((SPI_AIN_Cfg_v_ctrl_ch[channel_index] & 0x40) == 0) ? "BIPOLAR" : "Unipolar"
                                            case 'U': case 'u':
                                                SPI_AIN_Cfg_v_ctrl_ch[channel_index] = SPI_AIN_Cfg_v_ctrl_ch[channel_index] | 0x40;
                                                break;
                                            //
                                            // cmdLine.serial().print(F("\r\n LS5CB -- Datalog SPI ADC channel channel 5 v_ctrl Bipolar"));
                                            // ((SPI_AIN_Cfg_v_ctrl_ch[channel_index] & 0x40) == 0) ? "BIPOLAR" : "Unipolar"
                                            case 'B': case 'b':
                                                SPI_AIN_Cfg_v_ctrl_ch[channel_index] = SPI_AIN_Cfg_v_ctrl_ch[channel_index] &~ 0x40;
                                                break;
                                        }
                                        break;
                                    }
                                    //
                                    //
                                }
                            } // end if cmdLine[2] channel_index
                        } // end for channel_index
                        Datalogger_PrintHeader();
                        Datalogger_Need_PrintHeader = true;
                    }
                    break;
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
#if defined(LOG_PLATFORM_AIN) // Datalog Arduino platform analog inputs
                // LA<channel ID><verb>: Configure Platform_AIN channels
                // channel ID: 0,1,2,... or - for all channels
                // verb: D for disable, V for Voltage, L for LSB
                case 'A': case 'a':
                    {
                        // all-channel: loop through all valid channel_index, test cmdLine[2] 'is it me'?
                        // for channel_index loop through 0..NUM_DUT_ANALOG_IN_CHANNELS
                        //   if ((cmdLine[2] == '-' /* all channels */) 
                        //   ||  (cmdLine[2] == '0'+channel_index)
                        //      ) {
                        //       // it's me
                        //       // test cmdLine[3] to determine action
                        //       // Platform_Enable_ch[channel_index] = Platform_AIN_xxxxx;
                        //   } end if cmdLine[2] channel_index
                        // } end for channel_index
                        for (int channel_index = 0; channel_index < NUM_PLATFORM_ANALOG_IN_CHANNELS; channel_index++) {
                            if ((cmdLine[2] == '-' /* all channels */) 
                            ||  (cmdLine[2] == '0'+channel_index)
                               )
                            {
                                // it's me
                                // test cmdLine[3] to determine action
                                // Platform_Enable_ch[channel_index] = Platform_AIN_xxxxx;
                                switch (cmdLine[3])
                                {
                                    case 'D': case 'd':
                                        Platform_Enable_ch[channel_index] = Platform_AIN_Disable;
                                        break;
#if LOG_PLATFORM_ANALOG_IN_LSB
                                    case 'L': case 'l':
                                        Platform_Enable_ch[channel_index] = Platform_AIN_Enable_LSB;
                                        break;
#endif // LOG_PLATFORM_ANALOG_IN_LSB
#if LOG_PLATFORM_ANALOG_IN_VOLTS
                                    case 'V': case 'v':
                                        Platform_Enable_ch[channel_index] = Platform_AIN_Enable_Volt;
                                        break;
#endif // LOG_PLATFORM_ANALOG_IN_VOLTS
                                }
                            } // end if cmdLine[2] channel_index
                        } // end for channel_index
                        // Datalogger_PrintHeader(cmdLine);
                        if (Datalogger_enable_serial) {
                            Datalogger_PrintHeader(cmdLine);
                        }
        # if HAS_AUX_SERIAL
                        if (Datalogger_enable_AUXserial) {
                            Datalogger_PrintHeader(cmdLine_AUXserial);
                        }
        # endif // HAS_AUX_SERIAL
        # if HAS_DAPLINK_SERIAL
                        if (Datalogger_enable_DAPLINKserial) {
                            Datalogger_PrintHeader(cmdLine_DAPLINKserial);
                        }
        # endif // HAS_DAPLINK_SERIAL
                        Datalogger_Need_PrintHeader = true;
                    }
                    break;
#endif // defined(LOG_PLATFORM_AIN)
                case '>':
                    // L>A -- Datalogger_enable_AUXserial
                    // L>D -- Datalogger_enable_DAPLINKserial
                    // L>S -- Datalogger_enable_serial
                    switch (cmdLine[2])
                    {
# if HAS_AUX_SERIAL
                        case 'A': case 'a':
                            Datalogger_enable_AUXserial = !Datalogger_enable_AUXserial;
                            Datalogger_Need_PrintHeader = true;
                            break;
# endif // HAS_AUX_SERIAL
# if HAS_DAPLINK_SERIAL
                        case 'D': case 'd':
                            Datalogger_enable_DAPLINKserial = !Datalogger_enable_DAPLINKserial;
                            Datalogger_Need_PrintHeader = true;
                            break;
# endif // HAS_DAPLINK_SERIAL
                        case 'S': case 's':
                            Datalogger_enable_serial = !Datalogger_enable_serial;
                            Datalogger_Need_PrintHeader = true;
                            break;
                    }
                    break; // case '>' L>S serial enable toggle
            default:
                // TODO: L -- detailed help for datalog commands
                //
#if USE_DATALOGGER_TRIGGER // support Datalog trigger
#if 1 // USE_DATALOGGER_TRIGGER_HELP_BRIEF
                // Datalogger_Trigger = trigger_FreeRun // free run as fast as possible "always-on mode"
                cmdLine.serial().printf("\r\n LR -- Datalog free run as fast as possible");
                //
                // Datalogger_Trigger = trigger_Timer // timer (configure interval) "intermittent-sleep-mode"
                //~ cmdLine.serial().printf("\r\n LT -- Datalog timer"); // trigger_Timer
#if USE_DATALOGGER_SLEEP // support sleep modes in Datalogger_Trigger
                // TODO: USE_DATALOGGER_SLEEP -- support sleep modes in Datalogger_Trigger
                cmdLine.serial().printf("\r\n LT count=%d base=%dms sleep=%d -- Datalog timer",
                    g_timer_interval_count, 
                    g_timer_interval_msec,
                    (int)g_timer_sleepmode
                ); // trigger_Timer
#else // USE_DATALOGGER_SLEEP
                cmdLine.serial().printf("\r\n LT count=%d base=%dms -- Datalog timer", g_timer_interval_count, g_timer_interval_msec); // trigger_Timer
#endif // USE_DATALOGGER_SLEEP
                //
                // TODO: Datalogger_Trigger = trigger_PlatformDigitalInput // platform digital input (configure digital input pin reference)
                //~ cmdLine.serial().printf("\r\n LI -- Datalog _______"); // trigger_PlatformDigitalInput
                // TODO: cmdLine.serial().printf("\r\n LIH3 -- Datalog when input high D3"); // trigger_PlatformDigitalInput
                // TODO: cmdLine.serial().printf("\r\n LIL6 -- Datalog when input low D6"); // trigger_PlatformDigitalInput
                //
#if USE_DATALOGGER_SPIDeviceRegRead
                // TODO: Datalogger_Trigger = trigger_SPIDeviceRegRead // SPI device register read (configure regaddr, mask value, match value)
                //~ cmdLine.serial().printf("\r\n L$ -- Datalog _______"); // trigger_SPIDeviceRegRead
                cmdLine.serial().printf("\r\n L$ count=10 base=500ms addr=xxx data=xxx mask=xxx -- Datalog when SPI read of address matches mask"); // trigger_SPIDeviceRegRead
#endif // USE_DATALOGGER_SPIDeviceRegRead
                //
#if USE_DATALOGGER_ActionTable // Datalogger_RunActionTable() supported actions
                cmdLine.serial().printf("\r\n L@ -- configures Datalogger_action_table; show more commands");
#endif // USE_DATALOGGER_ActionTable Datalogger_RunActionTable() supported actions
                //
                //
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
                // LS<channel ID><verb>: Configure SPI_AIN channels
                // channel ID: 0,1,2,... or - for all channels
                // verb: D for disable, V for Voltage, L for LSB
                cmdLine.serial().printf("\r\n LS-D -- Datalog SPI ADC channel '-'(all) Disable");
                cmdLine.serial().printf("\r\n LS-V -- Datalog SPI ADC channel '-'(all) Volt");
                cmdLine.serial().printf("\r\n LS-L -- Datalog SPI ADC channel '-'(all) LSB");
                cmdLine.serial().printf("\r\n LS2D -- Datalog SPI ADC channel channel 2 Disable");
                cmdLine.serial().printf("\r\n LS3V -- Datalog SPI ADC channel channel 3 Volt");
                cmdLine.serial().printf("\r\n LS4L -- Datalog SPI ADC channel channel 4 LSB");
                //
                // MAX11410 verb for configuring SPI_AIN_Cfg_v_filter_ch[channel_index]
                // cmdLine.serial().print(F("\r\n LS-CF34 -- Datalog SPI ADC channel channel 5 v_filter 0x34"));
                cmdLine.serial().printf("\r\n LS-CF34 -- Datalog SPI ADC channel '-'(all) v_filter 0x34");
                //
                // MAX11410 verb for configuring SPI_AIN_Cfg_v_ctrl_ch[channel_index]
                // cmdLine.serial().print(F("\r\n LS-CC42 -- Datalog SPI ADC channel '-'(all) v_ctrl 0x42"));
                cmdLine.serial().printf("\r\n LS-CC42 -- Datalog SPI ADC channel '-'(all) v_ctrl 0x42");    
                //
                // MAX11410 verb for configuring SPI_AIN_Cfg_v_ctrl_ch[channel_index]
                // cmdLine.serial().print(F("\r\n LS5___ -- Datalog SPI ADC channel channel 5 v_ctrl"));
                // MAX11410 verb for configuring SPI_AIN_Cfg_v_ctrl_ch[channel_index]
                cmdLine.serial().printf("\r\n LS5CU -- Datalog SPI ADC channel channel 5 v_ctrl Unipolar");
                // ((SPI_AIN_Cfg_v_ctrl_ch[channel_index] & 0x40) == 0) ? "BIPOLAR" : "Unipolar"
                cmdLine.serial().printf("\r\n LS5CB -- Datalog SPI ADC channel channel 5 v_ctrl Bipolar");
                // ((SPI_AIN_Cfg_v_ctrl_ch[channel_index] & 0x40) == 0) ? "BIPOLAR" : "Unipolar"
                //
                // MAX11410 verb for configuring SPI_AIN_Cfg_v_pga_ch[channel_index]
                // cmdLine.serial().print(F("\r\n LS5CP00 -- Datalog SPI ADC channel channel 5 v_pga 0x00"));
                cmdLine.serial().printf("\r\n LS-CP00 -- Datalog SPI ADC channel '-'(all) v_pga 0x00");
                //
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
                //
#if defined(LOG_PLATFORM_AIN) // Datalog Arduino platform analog inputs
                // LA<channel ID><verb>: Configure Platform_AIN channels
                // channel ID: 0,1,2,... or - for all channels
                // verb: D for disable, V for Voltage, L for LSB
                cmdLine.serial().printf("\r\n LA-D -- Datalog Platform-AIN all-channel Disable");
#if LOG_PLATFORM_ANALOG_IN_VOLTS
                cmdLine.serial().printf("\r\n LA-V -- Datalog Platform-AIN all-channel Volt");
#endif // LOG_PLATFORM_ANALOG_IN_VOLTS
#if LOG_PLATFORM_ANALOG_IN_LSB
                cmdLine.serial().printf("\r\n LA-L -- Datalog Platform-AIN all-channel LSB");
#endif // LOG_PLATFORM_ANALOG_IN_LSB
                cmdLine.serial().printf("\r\n LA2D -- Datalog Platform-AIN channel 2 Disable");
#if LOG_PLATFORM_ANALOG_IN_VOLTS
                cmdLine.serial().printf("\r\n LA3V -- Datalog Platform-AIN channel 3 Volt");
#endif // LOG_PLATFORM_ANALOG_IN_VOLTS
#if LOG_PLATFORM_ANALOG_IN_LSB
                cmdLine.serial().printf("\r\n LA4L -- Datalog Platform-AIN channel 4 LSB");
#endif // LOG_PLATFORM_ANALOG_IN_LSB
#endif // defined(LOG_PLATFORM_AIN)
#endif // USE_DATALOGGER_TRIGGER support Datalog trigger
# if HAS_AUX_SERIAL
                cmdLine.serial().printf("\r\n L>A -- Datalogger_enable_AUXserial %s", (Datalogger_enable_AUXserial?"disable":"enable"));
# endif // HAS_AUX_SERIAL
# if HAS_DAPLINK_SERIAL
                cmdLine.serial().printf("\r\n L>D -- Datalogger_enable_DAPLINKserial %s", (Datalogger_enable_DAPLINKserial?"disable":"enable"));
# endif // HAS_DAPLINK_SERIAL
                cmdLine.serial().printf("\r\n L>S -- Datalogger_enable_serial %s", (Datalogger_enable_serial?"disable":"enable"));
                //
#else // USE_DATALOGGER_TRIGGER_HELP_BRIEF
#endif // USE_DATALOGGER_TRIGGER_HELP_BRIEF
                break;
            } // switch (cmdLine[1]) inside case 'L'
        }
        break;         // case 'L'
#endif // USE_DATALOGGER_TRIGGER support Datalog trigger
       //
       // Application-specific commands here
       // alphanumeric command codes A-Z,a-z,0-9 reserved for application use
       //
#if APPLICATION_ArduinoPinsMonitor
#endif // APPLICATION_ArduinoPinsMonitor

        //
        // TODO1: add new commands here
        //
        default:
#if APPLICATION_MAX5715 // main_menu_onEOLcommandParser print command prompt
            extern bool MAX5715_menu_onEOLcommandParser(CmdLine & cmdLine); // defined in Test_Menu_MAX5715.cpp
            if (!MAX5715_menu_onEOLcommandParser(cmdLine))
#elif APPLICATION_MAX11131 // main_menu_onEOLcommandParser print command prompt
            extern bool MAX11131_menu_onEOLcommandParser(CmdLine & cmdLine); // defined in Test_Menu_MAX11131.cpp
            if (!MAX11131_menu_onEOLcommandParser(cmdLine))
#elif APPLICATION_MAX5171 // main_menu_onEOLcommandParser print command prompt
            extern bool MAX5171_menu_onEOLcommandParser(CmdLine & cmdLine); // defined in Test_Menu_MAX5171.cpp
            if (!MAX5171_menu_onEOLcommandParser(cmdLine))
#elif APPLICATION_MAX11410 // main_menu_onEOLcommandParser print command prompt
            extern bool MAX11410_menu_onEOLcommandParser(CmdLine & cmdLine); // defined in Test_Menu_MAX11410.cpp
            if (!MAX11410_menu_onEOLcommandParser(cmdLine))
#elif APPLICATION_MAX12345 // main_menu_onEOLcommandParser print command prompt
            extern bool MAX12345_menu_onEOLcommandParser(CmdLine & cmdLine); // defined in Test_Menu_MAX12345.cpp
            if (!MAX12345_menu_onEOLcommandParser(cmdLine))
#else
            if (0) // not_handled_by_device_submenu
#endif
            {
                cmdLine.serial().printf("\r\n unknown command ");
                //~ cmdLine.serial().printf("\r\n unknown command 0x%2.2x \"%s\"\r\n", cmdLine.str()[0], cmdLine.str());
# if HAS_DAPLINK_SERIAL
                cmdLine_DAPLINKserial.serial().printf("\r\n unknown command 0x%2.2x \"%s\"\r\n",
                                                      cmdLine.str()[0], cmdLine.str());
# endif // HAS_DAPLINK_SERIAL
            }
    }     // switch (cmdLine[0])
//
    // TODO: avoid excess console noise when a Button event happens during datalogging -- quiet inside Run_command_table()
    if (g_Run_command_table_running == false) {
// print command prompt
    // print_command_prompt();
    cmdLine.serial().printf("\r\n> ");
    } // if (g_Run_command_table_running)
}
#endif // USE_CMDLINE_MENUS support CmdLine command menus

//--------------------------------------------------
// print column header banner for csv data columns
void Datalogger_PrintHeader(CmdLine& cmdLine)
{
#if MAX40108_DEMO // main_menu_status banner
    cmdLine.serial().printf("\r\n\r\n\"MAX40108_U%d", MAX40108_DEMO);
#ifdef BOARD_SERIAL_NUMBER
// data unique to certain boards based on serial number
    cmdLine.serial().printf(" [s/n %d]", BOARD_SERIAL_NUMBER);
#endif // BOARD_SERIAL_NUMBER data unique to certain boards based on serial number
#ifdef FW_REV
    cmdLine.serial().printf(" [FW_REV %d]", FW_REV);
#endif // FW_REV
    cmdLine.serial().printf("\"\r\n");
#endif // MAX40108_DEMO

    // column header banner for csv data columns
        int field_index = 0;
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
        for (int channel_index = 0; channel_index < NUM_DUT_ANALOG_IN_CHANNELS; channel_index++) {
            if (SPI_AIN_Enable_ch[channel_index] == SPI_AIN_Disable) {
                continue;
            }
            // comma between fields
            if (field_index > 0) {
                cmdLine.serial().printf(",");
            }
            field_index++;
            // AIN_index column header prefix
#if SPI_ADC_DeviceName == MAX11410 // SPI connected ADC
            // MAX11410 v_ctrl bipolar configuration or unipolar?
            if ((SPI_AIN_Cfg_v_ctrl_ch[channel_index] & 0x40) == 0) {
                cmdLine.serial().printf("\"AIN%d-%d_BIP", channel_index, channel_index+1);
            }
            else {
                cmdLine.serial().printf("\"AIN%d", channel_index);
            }
#else // SPI_ADC_DeviceName == MAX11410 // SPI connected ADC
            cmdLine.serial().printf("\"AIN%d", channel_index);
#endif // SPI_ADC_DeviceName == MAX11410 // SPI connected ADC
            if (SPI_AIN_Enable_ch[channel_index] == SPI_AIN_Enable_LSB) {
                // _LSB column header suffix
                cmdLine.serial().printf("_LSB");
# if HAS_DAPLINK_SERIAL
                cmdLine_DAPLINKserial.serial().printf("_LSB");
# endif // HAS_DAPLINK_SERIAL
            }
            else if (SPI_AIN_Enable_ch[channel_index] == SPI_AIN_Enable_Volt) {
                // _V column header suffix
                cmdLine.serial().printf("_V");
# if HAS_DAPLINK_SERIAL
                cmdLine_DAPLINKserial.serial().printf("_V");
# endif // HAS_DAPLINK_SERIAL
            }
#if HAS_SPI_AIN_customChannelHeader // Optional custom per-channel header suffix
            // Optional custom per-channel header suffix
            if (SPI_AIN_customChannelHeader_ch[channel_index] && SPI_AIN_customChannelHeader_ch[channel_index][0]) {
                // not a null pointer, and not an empty string
                cmdLine.serial().printf("_%s", SPI_AIN_customChannelHeader_ch[channel_index]);
# if HAS_DAPLINK_SERIAL
                cmdLine_DAPLINKserial.serial().printf("_%s", SPI_AIN_customChannelHeader_ch[channel_index]);
# endif // HAS_DAPLINK_SERIAL
            } else {
                // no custom channel name for this channel
                //~ cmdLine.serial().printf("~");
# if HAS_DAPLINK_SERIAL
                //~ cmdLine_DAPLINKserial.serial().printf("~");
# endif // HAS_DAPLINK_SERIAL
            }
#endif // HAS_SPI_AIN_customChannelHeader
            // close quote
            cmdLine.serial().printf("\"");
# if HAS_DAPLINK_SERIAL
            cmdLine_DAPLINKserial.serial().printf("\"");
# endif // HAS_DAPLINK_SERIAL
        }

#if VERIFY_PART_ID_IN_LOOP
        // PART_ID field: Device ID Validation
        cmdLine.serial().printf(",\"PART_ID\"");
# if HAS_DAPLINK_SERIAL
        cmdLine_DAPLINKserial.serial().printf(",\"PART_ID\"");
# endif // HAS_DAPLINK_SERIAL
#endif // VERIFY_PART_ID_IN_LOOP
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
#if defined(LOG_PLATFORM_AIN) // Datalog Arduino platform analog inputs
        for (int channel_index = 0; channel_index < NUM_PLATFORM_ANALOG_IN_CHANNELS; channel_index++) {
            if (Platform_Enable_ch[channel_index] == Platform_AIN_Disable) {
                continue;
            }
            // comma between fields
            if (field_index > 0) {
                cmdLine.serial().printf(",");
            }
            field_index++;
            // AIN_index column header prefix
            cmdLine.serial().printf("\"A%d", channel_index);
            if (Platform_Enable_ch[channel_index] == Platform_AIN_Enable_LSB) {
                // _LSB column header suffix
                cmdLine.serial().printf("_LSB");
            }
            if (Platform_Enable_ch[channel_index] == Platform_AIN_Enable_Volt) {
                // _V column header suffix
                cmdLine.serial().printf("_V");
            }
#if HAS_Platform_AIN_customChannelHeader // Optional custom per-channel header suffix
            // Optional custom per-channel header suffix
            if (Platform_AIN_customChannelHeader_ch[channel_index] && Platform_AIN_customChannelHeader_ch[channel_index][0]) {
                // not a null pointer, and not an empty string
                cmdLine.serial().printf("_%s", Platform_AIN_customChannelHeader_ch[channel_index]);
# if HAS_DAPLINK_SERIAL
                cmdLine_DAPLINKserial.serial().printf("_%s", Platform_AIN_customChannelHeader_ch[channel_index]);
# endif // HAS_DAPLINK_SERIAL
            } else {
                // no custom channel name for this channel
                //~ cmdLine.serial().printf("~");
# if HAS_DAPLINK_SERIAL
                //~ cmdLine_DAPLINKserial.serial().printf("~");
# endif // HAS_DAPLINK_SERIAL
            }
#endif // HAS_Platform_AIN_customChannelHeader
            // close quote
            cmdLine.serial().printf("\"");
# if HAS_DAPLINK_SERIAL
            cmdLine_DAPLINKserial.serial().printf("\"");
# endif // HAS_DAPLINK_SERIAL
        }
#endif // defined(LOG_PLATFORM_AIN)
#if USE_DATALOGGER_REMARK_FIELD
        // Datalogger_PrintRow() print gstrRemarkText field from recent #remark -- in Datalogger_PrintHeader
        cmdLine.serial().printf(",\"%s\"", gstrRemarkHeader);
#endif // USE_DATALOGGER_REMARK_FIELD
        // end of column header line
        cmdLine.serial().printf("\r\n");
# if HAS_DAPLINK_SERIAL
        cmdLine_DAPLINKserial.serial().printf("\r\n");
# endif // HAS_DAPLINK_SERIAL
} // void Datalogger_PrintHeader()

//--------------------------------------------------
void Datalogger_AcquireRow()
{
            // CODE GENERATOR: example code: has no member function ScanStandardExternalClock
            // CODE GENERATOR: example code: has no member function ReadAINcode
            // CODE GENERATOR: example code: member function Read_All_Voltages
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
            // Measure ADC channels in sequence from AIN0 to channelNumber_0_9.
            // @param[in] g_MAX11410_device.channelNumber_0_15: AIN Channel Number
            // @param[in] g_MAX11410_device.PowerManagement_0_2: 0=Normal, 1=AutoShutdown, 2=AutoStandby
            // @param[in] g_MAX11410_device.chan_id_0_1: ADC_MODE_CONTROL.CHAN_ID
            //~ int channelId_0_9 = NUM_DUT_ANALOG_IN_CHANNELS-1+1;
            //g_MAX11410_device.channelNumber_0_15 = channelId_0_9;
            //g_MAX11410_device.PowerManagement_0_2 = 0;
            //g_MAX11410_device.chan_id_0_1 = 1;
            //----------------------------------------
            // scan AIN0..AIN9
            //
#if 1
            g_MAX11410_device.v_filter = SPI_AIN_Cfg_v_filter_ch[0];
            g_MAX11410_device.v_ctrl = SPI_AIN_Cfg_v_ctrl_ch[0];
            g_MAX11410_device.v_pga = SPI_AIN_Cfg_v_pga_ch[0];
            //
            // diagnostic GPIO pulse on MAX11410 GP1 pin (0xc3 = logic 0, 0xc4 = logic 1)
            g_MAX11410_device.RegWrite(MAX11410::CMD_r000_0101_dddd_xddd_GP1_CTRL, 0xc3); // GP1 = 0
            //
            int field_index = 0;
            for (int channel_index = 0; channel_index < NUM_DUT_ANALOG_IN_CHANNELS; channel_index++) {
                if (SPI_AIN_Enable_ch[channel_index] == SPI_AIN_Disable) {
                    continue;
                }
                field_index++;
                g_MAX11410_device.v_filter = SPI_AIN_Cfg_v_filter_ch[channel_index];
                g_MAX11410_device.v_ctrl = SPI_AIN_Cfg_v_ctrl_ch[channel_index];
                g_MAX11410_device.v_pga = SPI_AIN_Cfg_v_pga_ch[channel_index];
                //
                // WIP SampleRate_of_FILTER_CONV_START() MAX11410EMC-FW slow ODR 10Sps #262
                // adjust the MAX11410.loop_limit value if the sample rate is set to a value slower than 20sps
                // SampleRate_of_FILTER_CONV_START(uint8_t FILTER_RegValue, uint8_t CONV_START_RegValue)
                double SampleRate = g_MAX11410_device.SampleRate_of_FILTER_CONV_START(g_MAX11410_device.v_filter, MAX11410::MAX11410_CONV_TYPE_enum_t::CONV_TYPE_01_Continuous);
                if (SampleRate < 20.0) {
                    g_MAX11410_device.loop_limit = 32767; // TODO: is this timeout long enough for the slow output data rates?
                }
                //
                // diagnostic GPIO pulse on MAX11410 GP0 pin (0xc3 = logic 0, 0xc4 = logic 1)
                g_MAX11410_device.RegWrite(MAX11410::CMD_r000_0100_dddd_xddd_GP0_CTRL, 0xc3); // GP0 = 0
                //
#if SPI_ADC_DeviceName == MAX11410 // SPI connected ADC
                // MAX11410 v_ctrl bipolar configuration or unipolar?
                if ((SPI_AIN_Cfg_v_ctrl_ch[channel_index] & 0x40) == 0) {
                    const MAX11410::MAX11410_AINP_SEL_enum_t ainp = (MAX11410::MAX11410_AINP_SEL_enum_t)(channel_index);
                    const MAX11410::MAX11410_AINN_SEL_enum_t ainn = (MAX11410::MAX11410_AINN_SEL_enum_t)(channel_index^1);
                    SPI_AIN_Voltage[channel_index] = g_MAX11410_device.Measure_Voltage(ainp, ainn);
                    // @post AINcode[ainp]: measurement result LSB code
                }
                else {
                    const MAX11410::MAX11410_AINP_SEL_enum_t ainp = (MAX11410::MAX11410_AINP_SEL_enum_t)(channel_index);
                    const MAX11410::MAX11410_AINN_SEL_enum_t ainn = MAX11410::AINN_SEL_1010_GND;
                    SPI_AIN_Voltage[channel_index] = g_MAX11410_device.Measure_Voltage(ainp, ainn);
                    // @post AINcode[ainp]: measurement result LSB code
                }
#endif // SPI_ADC_DeviceName == MAX11410 // SPI connected ADC
                //
                // diagnostic GPIO pulse on MAX11410 GP0 pin (0xc3 = logic 0, 0xc4 = logic 1)
                g_MAX11410_device.RegWrite(MAX11410::CMD_r000_0100_dddd_xddd_GP0_CTRL, 0xc4); // GP0 = 1
                //
            }
            // diagnostic GPIO pulse on MAX11410 GP1 pin (0xc3 = logic 0, 0xc4 = logic 1)
            g_MAX11410_device.RegWrite(MAX11410::CMD_r000_0101_dddd_xddd_GP1_CTRL, 0xc4); // GP1 = 1
#else
            g_MAX11410_device.Read_All_Voltages();
#endif
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
#if defined(LOG_PLATFORM_AIN) // Datalog Arduino platform analog inputs
            // mbed
            // Platform board uses simple analog inputs
#if LOG_PLATFORM_ANALOG_IN_LSB
            Platform_LSB[0] = analogIn0.read();
            Platform_LSB[1] = analogIn1.read();
            Platform_LSB[2] = analogIn2.read();
            Platform_LSB[3] = analogIn3.read();
            Platform_LSB[4] = analogIn4.read();
            Platform_LSB[5] = analogIn5.read();
#endif
#if LOG_PLATFORM_ANALOG_IN_VOLTS
#if USE_Platform_AIN_Average
            // #if USE_Platform_AIN_Average -- for (i=0; i<Platform_AIN_Average_N; i++) -- analogIn012345.read()
            // float normValue_0_1 = analogInPin.read(); but mean of Platform_AIN_Average_N samples
            float analogIn0_normValue_0_1 = 0;
            float analogIn1_normValue_0_1 = 0;
            float analogIn2_normValue_0_1 = 0;
            float analogIn3_normValue_0_1 = 0;
            float analogIn4_normValue_0_1 = 0;
            float analogIn5_normValue_0_1 = 0;
            for (int i = 0; i < Platform_AIN_Average_N; i++) {
                analogIn0_normValue_0_1 = analogIn0_normValue_0_1 + analogIn0.read();
                analogIn1_normValue_0_1 = analogIn1_normValue_0_1 + analogIn1.read();
                analogIn2_normValue_0_1 = analogIn2_normValue_0_1 + analogIn2.read();
                analogIn3_normValue_0_1 = analogIn3_normValue_0_1 + analogIn3.read();
                analogIn4_normValue_0_1 = analogIn4_normValue_0_1 + analogIn4.read();
                analogIn5_normValue_0_1 = analogIn5_normValue_0_1 + analogIn5.read();
            }
            analogIn0_normValue_0_1 = analogIn0_normValue_0_1 / Platform_AIN_Average_N;
            analogIn1_normValue_0_1 = analogIn1_normValue_0_1 / Platform_AIN_Average_N;
            analogIn2_normValue_0_1 = analogIn2_normValue_0_1 / Platform_AIN_Average_N;
            analogIn3_normValue_0_1 = analogIn3_normValue_0_1 / Platform_AIN_Average_N;
            analogIn4_normValue_0_1 = analogIn4_normValue_0_1 / Platform_AIN_Average_N;
            analogIn5_normValue_0_1 = analogIn5_normValue_0_1 / Platform_AIN_Average_N;
#else  // USE_Platform_AIN_Average
            float analogIn0_normValue_0_1 = analogIn0.read();
            float analogIn1_normValue_0_1 = analogIn1.read();
            float analogIn2_normValue_0_1 = analogIn2.read();
            float analogIn3_normValue_0_1 = analogIn3.read();
            float analogIn4_normValue_0_1 = analogIn4.read();
            float analogIn5_normValue_0_1 = analogIn5.read();
#endif // USE_Platform_AIN_Average
#if HAS_Platform_AIN_Calibration
            // apply calibration to normValue_0_1 value
            Platform_Voltage[0] = CalibratedNormValue(0, analogIn0_normValue_0_1) * adc_full_scale_voltage[0];
            Platform_Voltage[1] = CalibratedNormValue(1, analogIn1_normValue_0_1) * adc_full_scale_voltage[1];
            Platform_Voltage[2] = CalibratedNormValue(2, analogIn2_normValue_0_1) * adc_full_scale_voltage[2];
            Platform_Voltage[3] = CalibratedNormValue(3, analogIn3_normValue_0_1) * adc_full_scale_voltage[3];
            Platform_Voltage[4] = CalibratedNormValue(4, analogIn4_normValue_0_1) * adc_full_scale_voltage[4];
            Platform_Voltage[5] = CalibratedNormValue(5, analogIn5_normValue_0_1) * adc_full_scale_voltage[5];
#else // HAS_Platform_AIN_Calibration
            Platform_Voltage[0] = analogIn0_normValue_0_1 * adc_full_scale_voltage[0];
            Platform_Voltage[1] = analogIn1_normValue_0_1 * adc_full_scale_voltage[1];
            Platform_Voltage[2] = analogIn2_normValue_0_1 * adc_full_scale_voltage[2];
            Platform_Voltage[3] = analogIn3_normValue_0_1 * adc_full_scale_voltage[3];
            Platform_Voltage[4] = analogIn4_normValue_0_1 * adc_full_scale_voltage[4];
            Platform_Voltage[5] = analogIn5_normValue_0_1 * adc_full_scale_voltage[5];
#endif // HAS_Platform_AIN_Calibration
#endif // LOG_PLATFORM_ANALOG_IN_VOLTS
#endif // defined(LOG_PLATFORM_AIN) // Datalog Arduino platform analog inputs

#if VERIFY_PART_ID_IN_LOOP
            // PART_ID field: Device ID Validation
            const uint32_t part_id_expect = 0x000F02;
            uint32_t part_id_readback;
            g_MAX11410_device.RegRead(MAX11410::CMD_r001_0001_xxxx_xxxx_xxxx_xxxx_xxxx_xddd_PART_ID, &part_id_readback);
#endif // VERIFY_PART_ID_IN_LOOP
} // void Datalogger_AcquireRow()

#if USE_DATALOGGER_ActionTable // Datalogger_RunActionTable() supported actions
//--------------------------------------------------
// TODO: Datalogger_RunActionTable() between Datalogger_AcquireRow() and Datalogger_PrintRow()
void Datalogger_RunActionTable()
{
    if (Datalogger_action_table_enabled == false) {
        return;
    }
    // TODO: verbose Datalogger_RunActionTable() emit a csv column ,"Action"
    // TODO: assert Datalogger_action_table_row_count < ACTION_TABLE_ROW_MAX
    // for Datalogger_action_table[0..Datalogger_action_table_row_count]
    for (int i = 0; i < Datalogger_action_table_row_count; i++)
    {
        // skip if Datalogger_action_table[i].action == action_noop
        if (Datalogger_action_table[i].action == action_noop) {
            continue;
        }
        // TODO: test Datalogger_action_table[i].condition "if="
        // TODO: Could the .condition field also distinguish Platform ADC from SPI ADC?
        //   That way we keep both sets of ADC channels in separate lists
        //   if=0 -- always
        //   if=1,2,3,4,5,6 -- Platform_Voltage[ch] > < == >= <= != threshold
        //   if=7,8,9,10,11,12 -- SPI_AIN_Voltage[ch] > < == >= <= != threshold
        // also, are we comparing code or voltage?
        // TODO: selected Datalogger_action_table[i].condition_channel
        // Datalogger could have both platform ADC and external SPI ADC channels
        // if SPI_ADC_DeviceName 
        //   NUM_DUT_ANALOG_IN_CHANNELS
        //   SPI_AIN_Enable_ch[]
        //   SPI_AIN_customChannelHeader_ch[]
        //   SPI_AIN_Voltage[]
        // if LOG_PLATFORM_AIN 
        //   NUM_PLATFORM_ANALOG_IN_CHANNELS
        //   analogInPin_fullScaleVoltage[]
        //   Platform_Enable_ch[]
        //   Platform_AIN_customChannelHeader_ch[]
        //   Platform_Voltage[]
        // TODO: selected Datalogger_action_table[i].condition_threshold
        switch(Datalogger_action_table[i].condition)
        {
            case condition_always:
                //~ cmdLine.serial().printf(" always");
                break;
            case condition_if_An_gt_threshold:
                //~ cmdLine.serial().printf(" if A%d > %f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
#if defined(LOG_PLATFORM_AIN) // Datalog Arduino platform analog inputs
                if (!(Platform_Voltage[Datalogger_action_table[i].condition_channel] > Datalogger_action_table[i].condition_threshold)) {
                    continue;
                }
#endif // defined(LOG_PLATFORM_AIN)
                break;
            case condition_if_An_lt_threshold:
                //~ cmdLine.serial().printf(" if A%d < %f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
#if defined(LOG_PLATFORM_AIN) // Datalog Arduino platform analog inputs
                if (!(Platform_Voltage[Datalogger_action_table[i].condition_channel] < Datalogger_action_table[i].condition_threshold)) {
                    continue;
                }
#endif // defined(LOG_PLATFORM_AIN)
                break;
            case condition_if_An_eq_threshold:
                //~ cmdLine.serial().printf(" if A%d == %f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
#if defined(LOG_PLATFORM_AIN) // Datalog Arduino platform analog inputs
                if (!(Platform_Voltage[Datalogger_action_table[i].condition_channel] == Datalogger_action_table[i].condition_threshold)) {
                    continue;
                }
#endif // defined(LOG_PLATFORM_AIN)
                break;
            case condition_if_An_ge_threshold:
                //~ cmdLine.serial().printf(" if A%d >= %f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
#if defined(LOG_PLATFORM_AIN) // Datalog Arduino platform analog inputs
                if (!(Platform_Voltage[Datalogger_action_table[i].condition_channel] >= Datalogger_action_table[i].condition_threshold)) {
                    continue;
                }
#endif // defined(LOG_PLATFORM_AIN)
                break;
            case condition_if_An_le_threshold:
                //~ cmdLine.serial().printf(" if A%d <= %f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
#if defined(LOG_PLATFORM_AIN) // Datalog Arduino platform analog inputs
                if (!(Platform_Voltage[Datalogger_action_table[i].condition_channel] <= Datalogger_action_table[i].condition_threshold)) {
                    continue;
                }
#endif // defined(LOG_PLATFORM_AIN)
                break;
            case condition_if_An_ne_threshold:
                //~ cmdLine.serial().printf(" if A%d != %f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
#if defined(LOG_PLATFORM_AIN) // Datalog Arduino platform analog inputs
#endif // defined(LOG_PLATFORM_AIN)
                if (!(Platform_Voltage[Datalogger_action_table[i].condition_channel] != Datalogger_action_table[i].condition_threshold)) {
                    continue;
                }
                break;
            case condition_if_AINn_gt_threshold:
                //~ cmdLine.serial().printf(" if AIN%d > %f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
                if (!(SPI_AIN_Voltage[Datalogger_action_table[i].condition_channel] > Datalogger_action_table[i].condition_threshold)) {
                    continue;
                }
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
                break;
            case condition_if_AINn_lt_threshold:
                //~ cmdLine.serial().printf(" if AIN%d < %f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
                if (!(SPI_AIN_Voltage[Datalogger_action_table[i].condition_channel] < Datalogger_action_table[i].condition_threshold)) {
                    continue;
                }
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
                break;
            case condition_if_AINn_eq_threshold:
                //~ cmdLine.serial().printf(" if AIN%d == %f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
                if (!(SPI_AIN_Voltage[Datalogger_action_table[i].condition_channel] == Datalogger_action_table[i].condition_threshold)) {
                    continue;
                }
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
                break;
            case condition_if_AINn_ge_threshold:
                //~ cmdLine.serial().printf(" if AIN%d >= %f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
                if (!(SPI_AIN_Voltage[Datalogger_action_table[i].condition_channel] >= Datalogger_action_table[i].condition_threshold)) {
                    continue;
                }
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
                break;
            case condition_if_AINn_le_threshold:
                //~ cmdLine.serial().printf(" if AIN%d <= %f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
                if (!(SPI_AIN_Voltage[Datalogger_action_table[i].condition_channel] <= Datalogger_action_table[i].condition_threshold)) {
                    continue;
                }
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
                break;
            case condition_if_AINn_ne_threshold:
                //~ cmdLine.serial().printf(" if AIN%d != %f", Datalogger_action_table[i].condition_channel, Datalogger_action_table[i].condition_threshold);
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
                if (!(SPI_AIN_Voltage[Datalogger_action_table[i].condition_channel] != Datalogger_action_table[i].condition_threshold)) {
                    continue;
                }
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
                break;
        } // switch(Datalogger_action_table[i].condition)
        // selected Datalogger_action_table[i].digitalOutPin
        // perform selected Datalogger_action_table[i].action
        switch(Datalogger_action_table[i].action)
        {
            case action_noop:
                //~ cmdLine.serial().printf("No_Operation");
                break;
            case action_digitalOutLow:
            {
                //~ cmdLine.serial().printf("digitalOutLow");
                //~ cmdLine.serial().printf(" D%d", Datalogger_action_table[i].digitalOutPin);
                // perform action digitalOutLow
                int pinIndex = Datalogger_action_table[i].digitalOutPin;
#if ARDUINO_STYLE
                pinMode(pinIndex, OUTPUT);             // digital pins 0, 1, 2, .. 13, analog input pins A0, A1, .. A5
                digitalWrite(pinIndex, LOW);             // digital pins 0, 1, 2, .. 13, analog input pins A0, A1, .. A5
#else
                DigitalInOut& digitalInOutPin = find_digitalInOutPin(pinIndex);
                digitalInOutPin.output();
                digitalInOutPin.write(0);
#endif
            }
                break;
            case action_digitalOutHigh:
            {
                //~ cmdLine.serial().printf("digitalOutHigh");
                //~ cmdLine.serial().printf(" D%d", Datalogger_action_table[i].digitalOutPin);
                // perform action digitalOutHigh
                int pinIndex = Datalogger_action_table[i].digitalOutPin;
#if ARDUINO_STYLE
                pinMode(pinIndex, OUTPUT);             // digital pins 0, 1, 2, .. 13, analog input pins A0, A1, .. A5
                digitalWrite(pinIndex, HIGH);             // digital pins 0, 1, 2, .. 13, analog input pins A0, A1, .. A5
#else
                DigitalInOut& digitalInOutPin = find_digitalInOutPin(pinIndex);
                digitalInOutPin.output();
                digitalInOutPin.write(1);
#endif
            }
                break;
            case action_button:  // pin = button index 1, 2, 3
            {
                // don't allow onButton1FallingEdge() command processing to halt the datalog
                Datalogger_Trigger_enum_t saved_Datalogger_Trigger = Datalogger_Trigger;
#if HAS_BUTTON1_DEMO_INTERRUPT
                switch (Datalogger_action_table[i].digitalOutPin) // pin = button index 1, 2, 3
                {
                    case 1:
                        onButton1FallingEdge();
                        break;
#if HAS_BUTTON2_DEMO_INTERRUPT
                    case 2:
                        onButton2FallingEdge();
                        break;
#endif // HAS_BUTTON2_DEMO_INTERRUPT
#if HAS_BUTTON3_DEMO_INTERRUPT
                    case 3:
                        onButton3FallingEdge();
                        break;
#endif // HAS_BUTTON3_DEMO_INTERRUPT
#if HAS_BUTTON4_DEMO_INTERRUPT
                    case 4:
                        onButton4FallingEdge();
                        break;
#endif // HAS_BUTTON4_DEMO_INTERRUPT
#if HAS_BUTTON5_DEMO_INTERRUPT
                    case 5:
                        onButton5FallingEdge();
                        break;
#endif // HAS_BUTTON5_DEMO_INTERRUPT
#if HAS_BUTTON6_DEMO_INTERRUPT
                    case 6:
                        onButton6FallingEdge();
                        break;
#endif // HAS_BUTTON6_DEMO_INTERRUPT
#if HAS_BUTTON7_DEMO_INTERRUPT
                    case 7:
                        onButton7FallingEdge();
                        break;
#endif // HAS_BUTTON7_DEMO_INTERRUPT
#if HAS_BUTTON8_DEMO_INTERRUPT
                    case 8:
                        onButton8FallingEdge();
                        break;
#endif // HAS_BUTTON8_DEMO_INTERRUPT
#if HAS_BUTTON9_DEMO_INTERRUPT
                    case 9:
                        onButton9FallingEdge();
                        break;
#endif // HAS_BUTTON9_DEMO_INTERRUPT
                } // switch (Datalogger_action_table[i].digitalOutPin) // pin = button index 1, 2, 3
#endif // HAS_BUTTON1_DEMO_INTERRUPT
                // don't allow onButton1FallingEdge() command processing to halt the datalog
                Datalogger_Trigger = saved_Datalogger_Trigger;
            } // case action_button
                break;
            case action_trigger_Halt:
                //~ cmdLine.serial().printf("trigger_Halt");
                // perform action trigger_Halt
                Datalogger_Trigger = trigger_Halt;
                //~ Datalogger_Need_PrintHeader = true;
                break;
            case action_trigger_FreeRun:
                //~ cmdLine.serial().printf("trigger_FreeRun");
                // perform action trigger_FreeRun
                Datalogger_Trigger = trigger_FreeRun;
                //~ Datalogger_Need_PrintHeader = true;
                break;
            case action_trigger_Timer:
                //~ cmdLine.serial().printf("trigger_Timer");
                //~ // print configuration for trigger_Timer
                //~ cmdLine.serial().printf("(%d x %dmsec)", g_timer_interval_count, g_timer_interval_msec);
                // perform action trigger_Timer
                Datalogger_Trigger = trigger_Timer;
                //~ Datalogger_Need_PrintHeader = true;
                break;
            case action_trigger_PlatformDigitalInput:
                //~ cmdLine.serial().printf("trigger_PlatformDigitalInput");
                //~ // TODO: print configuration for trigger_PlatformDigitalInput
                //~ cmdLine.serial().printf("(%d)", g_config_PlatformDigitalInput);
                // TODO: perform action action_trigger_PlatformDigitalInput
                Datalogger_Trigger = trigger_PlatformDigitalInput;
                //~ Datalogger_Need_PrintHeader = true;
                break;
            case action_trigger_SPIDeviceRegRead:
                //~ cmdLine.serial().printf("trigger_SPIDeviceRegRead");
                //~ // TODO: print configuration for trigger_SPIDeviceRegRead
                //~ cmdLine.serial().printf("(%d)", g_config_SPIDeviceRegRead);
                // TODO: perform action action_trigger_SPIDeviceRegRead
                Datalogger_Trigger = trigger_SPIDeviceRegRead;
                //~ Datalogger_Need_PrintHeader = true;
                break;
        } // switch(Datalogger_action_table[i].action)
        // consider next row of action table
    } // for ...Datalogger_action_table_row_count
}
#endif // USE_DATALOGGER_ActionTable Datalogger_RunActionTable() supported actions

//--------------------------------------------------
void Datalogger_PrintRow(CmdLine& cmdLine)
{
            int field_index = 0;
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
            for (int channel_index = 0; channel_index < NUM_DUT_ANALOG_IN_CHANNELS; channel_index++) {
                if (SPI_AIN_Enable_ch[channel_index] == SPI_AIN_Disable) {
                    continue;
                }
                // comma between fields
                if (field_index > 0) {
                    cmdLine.serial().printf(",");
                }
                field_index++;
                if (SPI_AIN_Enable_ch[channel_index] == Platform_AIN_Enable_LSB) {
                    cmdLine.serial().printf("%d", g_MAX11410_device.AINcode[channel_index]);
                }
                if (SPI_AIN_Enable_ch[channel_index] == Platform_AIN_Enable_Volt) {
                    // TODO: report Voltage instead of LSB
                    // Serial.print(SPI_AIN_Voltage[channel_index]);
                    static char strOutLineBuffer[16];
                    cmdLine.serial().printf("%6.6f", SPI_AIN_Voltage[channel_index]);
                }
            }
#if VERIFY_PART_ID_IN_LOOP
            // PART_ID field: Device ID Validation
            if (part_id_readback != part_id_expect) {
                cmdLine.serial().printf(",\"FAIL\"");
                need_reinit = true;
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
                g_MAX11410_device.Init();
                g_MAX11410_device.v_filter = SPI_AIN_Cfg_v_filter_ch[0];
                g_MAX11410_device.v_ctrl = SPI_AIN_Cfg_v_ctrl_ch[0];
                g_MAX11410_device.v_pga = SPI_AIN_Cfg_v_pga_ch[0];
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
            }
            else {
                cmdLine.serial().printf(",\"OK\"");
            }
#endif // VERIFY_PART_ID_IN_LOOP
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
#if defined(LOG_PLATFORM_AIN) // Datalog Arduino platform analog inputs
            for (int channel_index = 0; channel_index < NUM_PLATFORM_ANALOG_IN_CHANNELS; channel_index++) {
                if (Platform_Enable_ch[channel_index] == Platform_AIN_Disable) {
                    continue;
                }
                // comma between fields
                if (field_index > 0) {
                    cmdLine.serial().printf(",");
                }
                field_index++;
                if (Platform_Enable_ch[channel_index] == Platform_AIN_Enable_LSB) {
#if LOG_PLATFORM_ANALOG_IN_LSB
                    cmdLine.serial().printf("%u", Platform_LSB[channel_index]);
#endif
                }
                if (Platform_Enable_ch[channel_index] == Platform_AIN_Enable_Volt) {
#if LOG_PLATFORM_ANALOG_IN_VOLTS
                    // Datalog Volts omit V suffix from numbers #275
                    // because Excel graphs can't handle numbers that have a suffix.
                    cmdLine.serial().printf("%6.6f", Platform_Voltage[channel_index]);
#endif
                }
            }
#endif // defined(LOG_PLATFORM_AIN)
            if (need_reinit) {
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
                if (g_MAX11410_device.Init() == 0) {
                    //~ cmdLine.serial().printf(",\"Init() failed\"");
                } else {
                    //~ cmdLine.serial().printf(",\"Init() success\"");
#if USE_CUSTOM_REG_INIT // custom_reg_init_addr[], custom_reg_init_data[], custom_reg_init_count
                    // in Datalogger_PrintRow(), when part_id test fails, 
                    // apply list of custom register writes after re-init
                    // custom_reg_init_addr[], custom_reg_init_data[], custom_reg_init_count
                    for (unsigned int index = 0; index < custom_reg_init_count; index++) {
                        uint8_t regAddress = custom_reg_init_addr[index];
                        uint32_t regData = custom_reg_init_data[index];
                        cmdLine.serial().printf("*%s=0x%06.6x", g_MAX11410_device.RegName(regAddress), regData);
                        g_MAX11410_device.RegWrite((MAX11410::MAX11410_CMD_enum_t)regAddress, regData);
                    }
#endif // USE_CUSTOM_REG_INIT
                    g_MAX11410_device.v_filter = SPI_AIN_Cfg_v_filter_ch[0];
                    g_MAX11410_device.v_ctrl = SPI_AIN_Cfg_v_ctrl_ch[0];
                    g_MAX11410_device.v_pga = SPI_AIN_Cfg_v_pga_ch[0];
                    need_reinit = false;
                }
#else // defined(SPI_ADC_DeviceName) // SPI connected ADC
                need_reinit = false;
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
            }
#if USE_DATALOGGER_REMARK_FIELD
            // Datalogger_PrintRow() print gstrRemarkText field from recent #remark -- in void Datalogger_PrintRow()
            cmdLine.serial().printf(",\"%s\"", gstrRemarkText);
#endif // USE_DATALOGGER_REMARK_FIELD
            cmdLine.serial().printf("\r\n");
            if (need_reinit) {
                //~ delay(500); // platform_delay_ms 500ms timing delay function
            }
} // void Datalogger_PrintRow()

// example code main function
int main()
{
    // setup: put your setup code here, to run once
#if USE_CMDLINE_MENUS // support CmdLine command menus
    // Configure serial ports
    cmdLine.clear();
    //~ cmdLine.serial().printf("\r\n cmdLine.serial().printf test\r\n");
    cmdLine.onEOLcommandParser = main_menu_onEOLcommandParser;
    //~ cmdLine.diagnostic_led_EOF = diagnostic_led_EOF;
    /// CmdLine::set_immediate_handler(char, functionPointer_void_void_on_immediate_0x21);
    //~ cmdLine.on_immediate_0x21 = on_immediate_0x21;
    //~ cmdLine.on_immediate_0x7b = on_immediate_0x7b;
    //~ cmdLine.on_immediate_0x7d = on_immediate_0x7d;
# if HAS_DAPLINK_SERIAL
#if 0 // HARD CRASH -- USE_AUX_SERIAL_CMD_FORWARDING
    // Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257 -- init aux baud rate g_auxSerialCom_baud
    // TODO: if g_auxSerialCom_baud is other than the default 9600 baud,
    // then the auxiliary serial port baud rate should be updated.
# if HAS_AUX_SERIAL
# else
    // Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257 -- init aux baud rate g_auxSerialCom_baud
    DAPLINKserial.baud(g_auxSerialCom_baud);
# endif
#endif // USE_AUX_SERIAL_CMD_FORWARDING
    cmdLine_DAPLINKserial.clear();
    //~ cmdLine_DAPLINKserial.serial().printf("\r\n cmdLine_DAPLINKserial.serial().printf test\r\n");
    cmdLine_DAPLINKserial.onEOLcommandParser = main_menu_onEOLcommandParser;
    //~ cmdLine_DAPLINKserial.on_immediate_0x21 = on_immediate_0x21;
    //~ cmdLine_DAPLINKserial.on_immediate_0x7b = on_immediate_0x7b;
    //~ cmdLine_DAPLINKserial.on_immediate_0x7d = on_immediate_0x7d;
# endif
# if HAS_AUX_SERIAL
    // TX/RX auxiliary UART port cmdLine_AUXserial AUXserial
#if 0 // HARD CRASH -- USE_AUX_SERIAL_CMD_FORWARDING
    // Command forwarding to AUX serial TX/RX cmdLine_AUXserial #257 -- init aux baud rate g_auxSerialCom_baud
    // TODO: if g_auxSerialCom_baud is other than the default 9600 baud,
    // then the auxiliary serial port baud rate should be updated.
    AUXserial.baud(g_auxSerialCom_baud);
#endif // USE_AUX_SERIAL_CMD_FORWARDING
    cmdLine_AUXserial.clear();
    //~ cmdLine_AUXserial.serial().printf("\r\n cmdLine_AUXserial.serial().printf test\r\n");
    cmdLine_AUXserial.onEOLcommandParser = main_menu_onEOLcommandParser;
    //~ cmdLine_AUXserial.on_immediate_0x21 = on_immediate_0x21;
    //~ cmdLine_AUXserial.on_immediate_0x7b = on_immediate_0x7b;
    //~ cmdLine_AUXserial.on_immediate_0x7d = on_immediate_0x7d;
# endif // HAS_AUX_SERIAL
#endif // USE_CMDLINE_MENUS support CmdLine command menus

    // example code: serial port banner message
    wait(3); // 3000ms timing delay function, platform-specific
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
    cmdLine.serial().printf("\r\nDataLogger_MAX11410\r\n"); // instead of Hello_MAX11410
#else // defined(SPI_ADC_DeviceName) // SPI connected ADC
    cmdLine.serial().printf("\r\nInternal_DataLogger\r\n"); // instead of Hello_MAX11410
# if HAS_DAPLINK_SERIAL
    cmdLine_DAPLINKserial.serial().printf("\r\nInternal_DataLogger\r\n"); // instead of Hello_MAX11410
# endif // HAS_DAPLINK_SERIAL
# if HAS_AUX_SERIAL
    cmdLine_AUXserial.serial().printf("\r\nInternal_DataLogger\r\n"); // instead of Hello_MAX11410
# endif // HAS_AUX_SERIAL
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC

    // CODE GENERATOR: get spi properties from device
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
    if (g_SPI_SCLK_Hz > g_MAX11410_device.get_spi_frequency())
    {  // Device limits SPI SCLK frequency
        g_SPI_SCLK_Hz = g_MAX11410_device.get_spi_frequency();
        cmdLine.serial().printf("\r\nMAX11410 limits SPI SCLK frequency to %ld Hz\r\n", g_SPI_SCLK_Hz);

        g_MAX11410_device.Init();
    }
    if (g_MAX11410_device.get_spi_frequency() > g_SPI_SCLK_Hz)
    {  // Platform limits SPI SCLK frequency
        g_MAX11410_device.spi_frequency(g_SPI_SCLK_Hz);
        cmdLine.serial().printf("\r\nPlatform limits MAX11410 SPI SCLK frequency to %ld Hz\r\n", g_SPI_SCLK_Hz);

        g_MAX11410_device.Init();
    }
    // g_SPI_dataMode = g_MAX11410_device.get_spi_dataMode();
    while (g_MAX11410_device.Init() == 0)
    {
        wait(3); // 3000ms timing delay function, platform-specific
        cmdLine.serial().printf("\r\nMAX11410 Init failed; retry...\r\n");

    }

    //  ---------- CUSTOMIZED from MAX11410_Hello after g_MAX11410_device.Init() ----------
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
    g_MAX11410_device.v_filter = SPI_AIN_Cfg_v_filter_ch[0];
    g_MAX11410_device.v_pga = SPI_AIN_Cfg_v_pga_ch[0];
    g_MAX11410_device.v_ctrl = SPI_AIN_Cfg_v_ctrl_ch[0];
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
    //  ---------- CUSTOMIZED from MAX11410_Hello ----------
#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC
        // CODE GENERATOR: example code: has no member function REF
        // CODE GENERATOR: example code for ADC: repeat-forever convert and print conversion result, one record per line
        // CODE GENERATOR: ResolutionBits = 24
        // CODE GENERATOR: FScode = 0xffffff
        // CODE GENERATOR: NumChannels = 10
        // CODE GENERATOR: banner before DataLogHelloCppCodeList while(1)
#if defined(SPI_ADC_DeviceName) // SPI connected ADC
        cmdLine.serial().printf("v_filter = 0x%2.2x\r\n", g_MAX11410_device.v_filter);

        cmdLine.serial().printf("v_pga = 0x%2.2x\r\n", g_MAX11410_device.v_pga);

        cmdLine.serial().printf("v_ctrl = 0x%2.2x\r\n", g_MAX11410_device.v_ctrl);

#endif // defined(SPI_ADC_DeviceName) // SPI connected ADC

        // column header banner for csv data columns
        Datalogger_Need_PrintHeader = true;

#if USE_LEDS
#if defined(TARGET_MAX32630)
        led1 = LED_ON; led2 = LED_OFF; led3 = LED_OFF;     // diagnostic rbg led RED
        ThisThread::sleep_for(125); // [since mbed-os-5.10] vs Thread::wait(125);
        led1 = LED_OFF; led2 = LED_ON; led3 = LED_OFF;     // diagnostic rbg led GREEN
        ThisThread::sleep_for(125); // [since mbed-os-5.10] vs Thread::wait(125);
        led1 = LED_OFF; led2 = LED_OFF; led3 = LED_ON;     // diagnostic rbg led BLUE
        ThisThread::sleep_for(125); // [since mbed-os-5.10] vs Thread::wait(125);
        led1 = LED_ON; led2 = LED_ON; led3 = LED_ON;     // diagnostic rbg led RED+GREEN+BLUE=WHITE
        ThisThread::sleep_for(125); // [since mbed-os-5.10] vs Thread::wait(125);
        led1 = LED_OFF; led2 = LED_ON; led3 = LED_ON;     // diagnostic rbg led GREEN+BLUE=CYAN
        ThisThread::sleep_for(125); // [since mbed-os-5.10] vs Thread::wait(125);
        led1 = LED_ON; led2 = LED_OFF; led3 = LED_ON;     // diagnostic rbg led RED+BLUE=MAGENTA
        ThisThread::sleep_for(125); // [since mbed-os-5.10] vs Thread::wait(125);
        led1 = LED_ON; led2 = LED_ON; led3 = LED_OFF;     // diagnostic rbg led RED+GREEN=YELLOW
        ThisThread::sleep_for(125); // [since mbed-os-5.10] vs Thread::wait(125);
        led1 = LED_OFF; led2 = LED_OFF; led3 = LED_OFF;     // diagnostic rbg led BLACK
        ThisThread::sleep_for(125); // [since mbed-os-5.10] vs Thread::wait(125);
#elif defined(TARGET_MAX32625MBED) || defined(TARGET_MAX32625PICO) || defined(TARGET_MAX40108DEMOP2U9)
        led1 = LED_ON; led2 = LED_OFF; led3 = LED_OFF;     // diagnostic rbg led RED
        ThisThread::sleep_for(125); // [since mbed-os-5.10] vs Thread::wait(125);
        led1 = LED_OFF; led2 = LED_ON; led3 = LED_OFF;     // diagnostic rbg led GREEN
        ThisThread::sleep_for(125); // [since mbed-os-5.10] vs Thread::wait(125);
        led1 = LED_OFF; led2 = LED_OFF; led3 = LED_ON;     // diagnostic rbg led BLUE
        ThisThread::sleep_for(125); // [since mbed-os-5.10] vs Thread::wait(125);
        led1 = LED_ON; led2 = LED_ON; led3 = LED_ON;     // diagnostic rbg led RED+GREEN+BLUE=WHITE
        ThisThread::sleep_for(125); // [since mbed-os-5.10] vs Thread::wait(125);
        led1 = LED_OFF; led2 = LED_ON; led3 = LED_ON;     // diagnostic rbg led GREEN+BLUE=CYAN
        ThisThread::sleep_for(125); // [since mbed-os-5.10] vs Thread::wait(125);
        led1 = LED_ON; led2 = LED_OFF; led3 = LED_ON;     // diagnostic rbg led RED+BLUE=MAGENTA
        ThisThread::sleep_for(125); // [since mbed-os-5.10] vs Thread::wait(125);
        led1 = LED_ON; led2 = LED_ON; led3 = LED_OFF;     // diagnostic rbg led RED+GREEN=YELLOW
        ThisThread::sleep_for(125); // [since mbed-os-5.10] vs Thread::wait(125);
        led1 = LED_OFF; led2 = LED_OFF; led3 = LED_OFF;     // diagnostic rbg led BLACK
        ThisThread::sleep_for(125); // [since mbed-os-5.10] vs Thread::wait(125);
#else // not defined(TARGET_LPC1768 etc.)
        led1 = LED_ON;
        led2 = LED_OFF;
        led3 = LED_OFF;
        led4 = LED_OFF;
        ThisThread::sleep_for(75); // [since mbed-os-5.10] vs Thread::wait(75);
        //led1 = LED_ON;
        led2 = LED_ON;
        ThisThread::sleep_for(75); // [since mbed-os-5.10] vs Thread::wait(75);
        led1 = LED_OFF;
        //led2 = LED_ON;
        led3 = LED_ON;
        ThisThread::sleep_for(75); // [since mbed-os-5.10] vs Thread::wait(75);
        led2 = LED_OFF;
        //led3 = LED_ON;
        led4 = LED_ON;
        ThisThread::sleep_for(75); // [since mbed-os-5.10] vs Thread::wait(75);
        led3 = LED_OFF;
        led4 = LED_ON;
        //
#endif // target definition
#endif

        if (led1.is_connected()) {
            led1 = LED_ON;
        }
        if (led2.is_connected()) {
            led2 = LED_ON;
        }
        if (led3.is_connected()) {
            led3 = LED_ON;
        }

        while(1) { // this code repeats forever
        // this code repeats forever

#if HAS_BUTTON1_DEMO_INTERRUPT_POLLING
        // avoid runtime error on button1 press [mbed-os-5.11]
        // instead of using InterruptIn, use DigitalIn and poll in main while(1)
# if HAS_BUTTON1_DEMO_INTERRUPT
        static int button1_value_prev = 1;
        static int button1_value_now = 1;
        button1_value_prev = button1_value_now;
        button1_value_now = button1.read();
        if ((button1_value_prev - button1_value_now) == 1)
        {
            // on button1 falling edge (button1 press)
            onButton1FallingEdge();
        }
# endif // HAS_BUTTON1_DEMO_INTERRUPT
# if HAS_BUTTON2_DEMO_INTERRUPT
        static int button2_value_prev = 1;
        static int button2_value_now = 1;
        button2_value_prev = button2_value_now;
        button2_value_now = button2.read();
        if ((button2_value_prev - button2_value_now) == 1)
        {
            // on button2 falling edge (button2 press)
            onButton2FallingEdge();
        }
# endif // HAS_BUTTON2_DEMO_INTERRUPT
# if HAS_BUTTON3_DEMO_INTERRUPT
        static int button3_value_prev = 1;
        static int button3_value_now = 1;
        button3_value_prev = button3_value_now;
        button3_value_now = button3.read();
        if ((button3_value_prev - button3_value_now) == 1)
        {
            // on button3 falling edge (button3 press)
            onButton3FallingEdge();
        }
# endif // HAS_BUTTON3_DEMO_INTERRUPT
#endif // HAS_BUTTON1_DEMO_INTERRUPT_POLLING
#if USE_CMDLINE_MENUS // support CmdLine command menus
            // TODO support CmdLine command menus (like on Serial_Tester); help and usual boilerplate
#if USE_AUX_SERIAL_CMD_FORWARDING
            // Command forwarding to Auxiliary serial port;
            // don't accept commands from Auxiliary serial port
#else // USE_AUX_SERIAL_CMD_FORWARDING
            // Accept commands from Auxiliary serial port
# if HAS_AUX_SERIAL
            if (AUXserial.readable()) {
                cmdLine_AUXserial.append(AUXserial.getc());
            }
# endif // HAS_AUX_SERIAL
# if HAS_DAPLINK_SERIAL
            if (DAPLINKserial.readable()) {
                cmdLine_DAPLINKserial.append(DAPLINKserial.getc());
            }
# endif // HAS_DAPLINK_SERIAL
#endif // USE_AUX_SERIAL_CMD_FORWARDING
            if (serial.readable()) {
                int c = serial.getc(); // instead of getc() or fgetc()
                cmdLine.append(c);
                // cmdLine.onEOLcommandParser handler implements menus
            } // if (Serial.available())
#endif // USE_CMDLINE_MENUS support CmdLine command menus

#if USE_DATALOGGER_TRIGGER // support Datalog trigger
            // Datalog trigger
            if (Datalogger_Trigger == trigger_Halt) {
                // halt the datalogger; continue accepting commands
                continue;
            }
            if (Datalogger_Trigger == trigger_FreeRun) {
                // free run as fast as possible
            }
            if (Datalogger_Trigger == trigger_Timer) {
                // timer (configure interval)
                // if Datalogger_Trigger == trigger_Timer sleep(g_timer_interval_msec)
                //
#if USE_DATALOGGER_SLEEP // support sleep modes in Datalogger_Trigger
// TODO: USE_DATALOGGER_SLEEP -- support sleep modes in Datalogger_Trigger
                // TODO: Sleep Mode vs Deep Sleep Mode
                // See documentation https://os.mbed.com/docs/mbed-os/v5.15/apis/power-management-sleep.html
                // ThisThread::sleep_for(uint32_t millisec)
                // sleep_manager_can_deep_sleep() // tests whether deep_sleep is locked or not
                // sleep_manager_lock_deep_sleep() // begin section that prevents deep_sleep
                // sleep_manager_unlock_deep_sleep() // end section that prevents deep_sleep
                switch(g_timer_sleepmode)
                {
                case datalogger_Sleep_LP0_Stop: // LT sleep = 0,
                    break;
                case datalogger_Sleep_LP1_DeepSleep: // LT sleep = 1,
                    // sleep_manager_can_deep_sleep(); // tests whether deep_sleep is locked or not
// TODO: USE_DATALOGGER_SLEEP -- ThisThread::sleep_for(uint32_t millisec) instead of wait_ms(g_timer_interval_msec)
                    // mbed_file_handle(STDIN_FILENO)->enable_input(false); // (mbed-os-5.15) platform.stdio-buffered-serial prevents deep sleep
                    cmdLine.serial().enable_input(false); // (mbed-os-5.15) platform.stdio-buffered-serial prevents deep sleep
                    // serial.enable_input(false); // (mbed-os-5.15) platform.stdio-buffered-serial prevents deep sleep
                    ThisThread::sleep_for(g_timer_interval_msec); // wait_ms(g_timer_interval_msec); // sleep during delay?
                    // mbed_file_handle(STDIN_FILENO)->enable_input(true); // (mbed-os-5.15)
                    cmdLine.serial().enable_input(true); // (mbed-os-5.15)
                    // serial.enable_input(true); // (mbed-os-5.15)
                    break;
                case datalogger_Sleep_LP2_Sleep: // LT sleep = 2,
                    sleep_manager_lock_deep_sleep(); // begin section that prevents deep_sleep
// TODO: USE_DATALOGGER_SLEEP -- ThisThread::sleep_for(uint32_t millisec) instead of wait_ms(g_timer_interval_msec)
                    ThisThread::sleep_for(g_timer_interval_msec); // wait_ms(g_timer_interval_msec); // sleep during delay?
                    sleep_manager_unlock_deep_sleep(); // end section that prevents deep_sleep
                    break;
                case datalogger_Sleep_LP3_Run: // LT sleep = 3,
// TODO: USE_DATALOGGER_SLEEP -- ThisThread::sleep_for(uint32_t millisec) instead of wait_ms(g_timer_interval_msec)
                    wait_ms(g_timer_interval_msec);
                    break;
                }
#else // USE_DATALOGGER_SLEEP
                wait_ms(g_timer_interval_msec); // sleep during delay?
#endif // USE_DATALOGGER_SLEEP
                //
                if (g_timer_interval_counter > 0) {
                    g_timer_interval_counter--;
                    continue;
                }
                // if time interval not yet reached, continue (continue accepting commands)
                g_timer_interval_counter = g_timer_interval_count-1;
            }
            if (Datalogger_Trigger == trigger_PlatformDigitalInput) {
                // platform digital input (configure digital input pin reference)
                // TODO: read selected input pin, test value
                // TODO: if no match, continue (continue accepting commands)
            }
            if (Datalogger_Trigger == trigger_SPIDeviceRegRead) {
                // SPI device register read (configure regaddr, mask value, match value)
                // TODO: SPI transfer regAddr
                // TODO: apply dataMask, compare with testValue
                // TODO: if no match, continue (continue accepting commands)
            }
#endif // USE_DATALOGGER_TRIGGER support Datalog trigger

            // column header banner for csv data columns
            if (Datalogger_Need_PrintHeader) {
                if (Datalogger_enable_serial) {
                    Datalogger_PrintHeader(cmdLine);
                }
# if HAS_AUX_SERIAL
                if (Datalogger_enable_AUXserial) {
                    Datalogger_PrintHeader(cmdLine_AUXserial);
                }
# endif // HAS_AUX_SERIAL
# if HAS_DAPLINK_SERIAL
                if (Datalogger_enable_DAPLINKserial) {
                    Datalogger_PrintHeader(cmdLine_DAPLINKserial);
                }
# endif // HAS_DAPLINK_SERIAL
                Datalogger_Need_PrintHeader = false;
            }
            Datalogger_AcquireRow();
#if USE_DATALOGGER_ActionTable // Datalogger_RunActionTable() supported actions
            // Datalogger_RunActionTable() between Datalogger_AcquireRow() and Datalogger_PrintRow()
            Datalogger_RunActionTable();
#endif // USE_DATALOGGER_ActionTable Datalogger_RunActionTable() supported actions
            // wait(3.0);
            // CODE GENERATOR: print conversion result
            // Use Arduino Serial Plotter to view output: Tools | Serial Plotter
            if (Datalogger_enable_serial) {
                Datalogger_PrintRow(cmdLine);
            }
# if HAS_AUX_SERIAL
            if (Datalogger_enable_AUXserial) {
                Datalogger_PrintRow(cmdLine_AUXserial);
            }
# endif // HAS_AUX_SERIAL
# if HAS_DAPLINK_SERIAL
            if (Datalogger_enable_DAPLINKserial) {
                Datalogger_PrintRow(cmdLine_DAPLINKserial);
            }
# endif // HAS_DAPLINK_SERIAL

        } // this code repeats forever
}
//---------- CODE GENERATOR: end DataLogHelloCppCodeList