Fork to see if I can get working

Dependencies:   BufferedSerial OneWire WinbondSPIFlash libxDot-dev-mbed5-deprecated

Fork of xDotBridge_update_test20180823 by Matt Briggs

xDotBridge/src/BaseboardIO.cpp

Committer:
mbriggs_vortex
Date:
2017-11-29
Revision:
100:0882cf295f8e
Parent:
99:83b54c851187

File content as of revision 100:0882cf295f8e:

/*
 * baseboardIO.cpp
 *
 *  Created on: Jan 25, 2017
 *      Author: mbriggs
 */

#include "BaseboardIO.h"
#include "dot_util.h" // XXX just need the reference to dot somehow
#include "xdot_low_power.h"
#include "MyLog.h"

// Original
//const float COIL_ON_TIME = 0.030; // 30 ms
// Test
const float COIL_ON_TIME = 0.300; // 300 ms


// Port expander 0 (Currently U7)
const uint8_t pEx0232En     = 0x01;
const uint8_t pEx0232TxDis  = 0x02;
const uint8_t pEx0Rot1B1    = 0x04;
const uint8_t pEx0Rot1B2    = 0x08;
const uint8_t pEx0Rot1B4    = 0x10;
const uint8_t pEx0Rot1B8    = 0x20;
const uint8_t pEx0Rot2B1    = 0x40;
const uint8_t pEx0Rot2B2    = 0x80;
const uint8_t pEx0OutMask   = 0x03; // Only allow bits 0,1 to be changed

// Port expander 1 (Currently U8)
const uint8_t pEx1NoNcSel   = 0x01;
const uint8_t pEx1RxTxSel   = 0x02;
const uint8_t pEx1WanSel    = 0x04;
const uint8_t pEx1SerialEn  = 0x08; // Labeled as reserved
const uint8_t pEx1Rot2B8    = 0x10;
const uint8_t pEx1Rot2B4    = 0x20;
const uint8_t pEx1RlyB      = 0x40; // This is actually a coil
const uint8_t pEx1RlyA      = 0x80; // This is actually a coil
const uint8_t pEx1OutMask   = 0xC0; // Only allow bits 6,7 to be changed

/**
 * Note for interrupt within uC cannot use two pins with the same numeric suffix (e.g. cannot
 * use both PA_0 and PB_0).  Note 1, 6, 7, 8, and 13 are used by LoRa radio.
 */

BaseboardIO::BaseboardIO()
    : mOWMaster(OneWireMasterPinName),
      mCCIn(CCInPinName),
      mTamper(TamperPinName),
      mPairBtn(PairBtnPinName),
      mLed(LedPinName),
      mLrrLed(LrrLedPinName, 0),
      mSwitchedIOCtrl(SwitchedIOCtrlPinName, 0)
{
    mPortExpanderVal0 = 0x00;
    mPortExpanderVal1 = 0x00;

    mPortEx0 = NULL;
    mPortEx1 = NULL;
}
CmdResult BaseboardIO::init(bool overwriteNvm)
{
    bool storedROMsGood = false;
    uint8_t val;
    // Setup port expanders
    if (readInfoFromNVM() == cmdSuccess && !overwriteNvm) {
        myLogInfo("Stored ROM0 Addr: %02x:%02x:%02x:%02x:%02x:%02x:%02x%02x found.",
                   mNvmObj.mPortExpanderROM0[7],
                   mNvmObj.mPortExpanderROM0[6],
                   mNvmObj.mPortExpanderROM0[5],
                   mNvmObj.mPortExpanderROM0[4],
                   mNvmObj.mPortExpanderROM0[3],
                   mNvmObj.mPortExpanderROM0[2],
                   mNvmObj.mPortExpanderROM0[1],
                   mNvmObj.mPortExpanderROM0[0]);
        myLogInfo("Stored ROM1 Addr: %02x:%02x:%02x:%02x:%02x:%02x:%02x%02x found.",
                   mNvmObj.mPortExpanderROM1[7],
                   mNvmObj.mPortExpanderROM1[6],
                   mNvmObj.mPortExpanderROM1[5],
                   mNvmObj.mPortExpanderROM1[4],
                   mNvmObj.mPortExpanderROM1[3],
                   mNvmObj.mPortExpanderROM1[2],
                   mNvmObj.mPortExpanderROM1[1],
                   mNvmObj.mPortExpanderROM1[0]);
        myLogInfo("BaseboardIO parameters successfully loaded from NVM");
        // Check that the ROM Addresses are correct and valid
        uint8_t portEx0Ctrl, portEx1Ctrl;
        mPortEx0 = new DS2408(&mOWMaster, mNvmObj.mPortExpanderROM0);
        mPortEx0->registerReadReliable(0x8D, portEx0Ctrl);
        // Gets 0xFF if it is not the correct address
        myLogInfo("PortEx0 Control register reads %02X", portEx0Ctrl);

        mPortEx1 = new DS2408(&mOWMaster, mNvmObj.mPortExpanderROM1);
        mPortEx1->registerReadReliable(0x8D, portEx1Ctrl);
        myLogInfo("PortEx1 Control register reads %02X", portEx1Ctrl);
        if ((portEx0Ctrl == 0xFF) || (portEx1Ctrl == 0xFF)) {
            myLogError("Stored port expander ROM check failed.  Set EEPROM to defaults.");
        }
        else {
            storedROMsGood = true;
        }
    }
    if (!storedROMsGood)
    { // EEPROM values not there or corrupt.  Should only happen in factory.
        mNvmObj.setDefaults();
        // Find ROM address and test which one is which.  Requires user
        // switches to be in known state.
        if (identifyPortExpanders() != cmdSuccess) {
            myLogError("Error identifying port expanders");
            return cmdError;
        }
        if (writeInfoToNVM() == cmdSuccess) {
            myLogInfo("Baseboard config saved to NVM");
        }
        else {
            myLogError("Baseboard config failed to save to NVM");
        }
        mPortEx0 = new DS2408(&mOWMaster, mNvmObj.mPortExpanderROM0);
        mPortEx0->registerReadReliable(0x8D, val);
        // Gets 0xFF if it is not the correct address
        myLogInfo("PortEx0 Control register reads %02X", val);
        mPortEx1 = new DS2408(&mOWMaster, mNvmObj.mPortExpanderROM1);
        mPortEx1->registerReadReliable(0x8D, val);
        myLogInfo("PortEx1 Control register reads %02X", val);
    }

    if (sampleUserSwitches() != cmdSuccess) {
        myLogError("Error sampling user switches");
        return cmdError;
    }

    // Put relay in known state
    if (relayNormal() != cmdSuccess) {
        myLogError("Error setting relay during init");
        return cmdError;
    }
    ledOff();

    myLogInfo("Baseboard IO initialization successful");
    return cmdSuccess;
}

// Registering for interrupts
void BaseboardIO::regCCInInt(Callback<void()> func)
{
    if (isCCNO()) {
        // Pulled high, switched low
        mCCIn.fall(func);
    }
    else {
        mCCIn.rise(func);
    }
    mCCIn.mode(PullNone);
    mCCIn.enable_irq();
}
void BaseboardIO::regTamperInt(Callback<void()> func)
{
    // Pulled high, switched low
    mTamper.mode(PullNone);
    mTamper.rise(func);
    mTamper.fall(func);
    mTamper.enable_irq();
}
void BaseboardIO::regPairBtnInt(Callback<void()> func)
{
    // Pulled low, switched high
    mPairBtn.mode(PullNone);
    mPairBtn.rise(func);
    mPairBtn.enable_irq();
}

// Input
CmdResult BaseboardIO::sampleUserSwitches()
{
    if ((mPortEx0 == NULL) || (mPortEx1 == NULL))
        return cmdError;
    // Sample port expanders
    enableSwitchedIO();
    wait(0.001);  // Wait 1 ms
    if (mPortEx0->pioLogicReliableRead(mPortExpanderVal0) != cmdSuccess) {
        disableSwitchedIO();
        myLogError("Error reading port expander 0.");
        return cmdError;
    }
    if (mPortEx1->pioLogicReliableRead(mPortExpanderVal1) != cmdSuccess) {
        disableSwitchedIO();
        myLogError("Error reading port expander 1.");
        return cmdError;
    }
    disableSwitchedIO();
    return cmdSuccess;
}
bool BaseboardIO::isCCInAlert()
{
    if (isCCNO()) { // If NO then the CCIn should float high if not in alert state
        return mCCIn == 0;
    }
    else { // If NC then the CCIN should be held low if not in alert state
        return mCCIn == 1;
    }
}
bool BaseboardIO::isPairBtn()
{
    // Depressed button is high
    return mPairBtn.read() == 1;
}
bool BaseboardIO::isCCNO()
{
    // When DIP switch is not closed (i.e. value reads high) assume NO
    return (mPortExpanderVal1 & pEx1NoNcSel) != 0; // Open NO, closed NC
}
void BaseboardIO::setIsCCNO(bool val)
{
    // When DIP switch is not closed (i.e. value reads high) assume NO
	if (val) {
		mPortExpanderVal1 |= pEx1NoNcSel; // Set bit
	}
	else {
		mPortExpanderVal1 &= ~pEx1NoNcSel; // Clear bit
	}
}
bool BaseboardIO::isRx()
{
    // When DIP switch is not closed (i.e. value reads high) assume RX
    return (mPortExpanderVal1 & pEx1RxTxSel) != 0;
}
void BaseboardIO::setIsRx(bool val)
{
    // When DIP switch is not closed (i.e. value reads high) assume RX
	if (val) {
		mPortExpanderVal1 |= pEx1RxTxSel; // Set bit
	}
	else {
		mPortExpanderVal1 &= ~pEx1RxTxSel; // Clear bit
	}
}
bool BaseboardIO::isLoRaWANMode()
{
    // When DIP switch is not closed (i.e. value reads high) assume P2P not WAN
    return (mPortExpanderVal1 & pEx1WanSel) == 0;
}
bool BaseboardIO::isSerialEnabled()
{
    // When DIP switch is not closed (i.e. value reads high) assume not in serial mode
    return (mPortExpanderVal1 & pEx1SerialEn) == 0;
}
uint8_t BaseboardIO::rotarySwitch1()
{
    // If a bit of a nibble is asserted then the port expander line is switched low.
    uint8_t val = 0;
    if ((mPortExpanderVal0 & pEx0Rot1B8) == 0)
        val |= 0x08;
    if ((mPortExpanderVal0 & pEx0Rot1B4) == 0)
        val |= 0x04;
    if ((mPortExpanderVal0 & pEx0Rot1B2) == 0)
        val |= 0x02;
    if ((mPortExpanderVal0 & pEx0Rot1B1) == 0)
        val |= 0x01;
    return val;
}
void BaseboardIO::setRotarySwitch1(uint8_t val)
{
	// Clear all then set
	mPortExpanderVal0 &= ~pEx0Rot1B8;
	mPortExpanderVal0 &= ~pEx0Rot1B4;
	mPortExpanderVal0 &= ~pEx0Rot1B2;
	mPortExpanderVal0 &= ~pEx0Rot1B1;
	
	if ((val & 0x08) == 0) {
		mPortExpanderVal0 |= pEx0Rot1B8;
	}
	if ((val & 0x04) == 0) {
		mPortExpanderVal0 |= pEx0Rot1B4;
	}
	if ((val & 0x02) == 0) {
		mPortExpanderVal0 |= pEx0Rot1B2;
	}
	if ((val & 0x01) == 0) {
		mPortExpanderVal0 |= pEx0Rot1B1;
	}
}
uint8_t BaseboardIO::rotarySwitch2()
{
    // If a bit of a nibble is asserted then the port expander line is switched low.
    uint8_t val = 0;
    if ((mPortExpanderVal1 & pEx1Rot2B8) == 0)
        val |= 0x08;
    if ((mPortExpanderVal1 & pEx1Rot2B4) == 0)
        val |= 0x04;
    if ((mPortExpanderVal0 & pEx0Rot2B2) == 0)
        val |= 0x02;
    if ((mPortExpanderVal0 & pEx0Rot2B1) == 0)
        val |= 0x01;
    return val;
}

// Output
CmdResult BaseboardIO::ledOn()
{
    mLed = 1;
#if ALSO_USE_LRR_LED
    mLrrLed = 1;
#endif
    return cmdSuccess;
}
CmdResult BaseboardIO::ledOff()
{
    mLed = 0;
    // Always allow setting GPIO0 to 0
//#if ALSO_USE_LRR_LED
    mLrrLed = 0;
//#endif
    return cmdSuccess;
}
CmdResult BaseboardIO::relayAlert()
{
    if (isCCNO()) { // Normally Open
        return closeRelay();
    }
    else { // Normally Close
        return openRelay();
    }
}
CmdResult BaseboardIO::relayNormal()
{
    if (isCCNO()) { // Normally Open
        return openRelay();
    }
    else { // Normally Close
        return closeRelay();
    }
}

// Future
CmdResult BaseboardIO::serialRx(bool enable)
{
    uint8_t val;
    if (mPortEx0 == NULL) {
        myLogError("Error enabling 232.  Port expanders not initialized.");
        return cmdError;
    }
    mPortEx0->pioLogicReliableRead(val);

    // Active low from port expander -> pmos -> 232 (active chip EN)
    if (enable) {
        val &= ~pEx0232En;
    }
    else {
        val |= pEx0232En;
    }

    if (mPortEx0->pioLogicReliableWrite(val | ~pEx0OutMask) != cmdSuccess) {
        myLogError("Error enabling 232");
        return cmdError;
    }
    return cmdSuccess;
}
CmdResult BaseboardIO::serialTx(bool enable)
{
    uint8_t val;
    if (mPortEx0 == NULL) {
        myLogError("Error enabling 232 TX.  Port expanders not initialized.");
        return cmdError;
    }
    mPortEx0->pioLogicReliableRead(val);

    // Active high tx disable therefore active low tx enable (note chip must also be enabled for TX)
    if (enable) {
        val &= ~pEx0232TxDis;
    }
    else {
        val |= pEx0232TxDis;
    }

    if (mPortEx0->pioLogicReliableWrite(val | ~pEx0OutMask) != cmdSuccess) {
        myLogError("Error enabling 232 TX");
        return cmdError;
    }
    return cmdSuccess;
}

// private
CmdResult BaseboardIO::readInfoFromNVM()
{
    bool nvmReadResult;
    uint8_t *data = new uint8_t [BASEBOARDIO_NVM_SIZE];

    nvmReadResult = dot->nvmRead(BASEBOARDIO_NVM_START_ADDR, data, BASEBOARDIO_NVM_SIZE);
    if (!nvmReadResult) {
        delete [] data;
        return cmdError;
    }
    mNvmObj.fromBytes(data, BASEBOARDIO_NVM_SIZE);
    delete [] data;
    if (!mNvmObj.validBaseboardIOFlag()) {
        myLogWarning("Invalid BaseboardIO Flag.  Using default values.");
        return cmdError;
    }
    else if (!mNvmObj.validBaseboardIORev()) {
        myLogWarning("Invalid BaseboardIO Rev.  Using default values.");
        return cmdError;
    }
    else {
        return cmdSuccess;
    }
}
CmdResult BaseboardIO::writeInfoToNVM()
{
    uint8_t *data = new uint8_t [BASEBOARDIO_NVM_SIZE];
    uint8_t size = BASEBOARDIO_NVM_SIZE;
    mNvmObj.toBytes(data, size);
    dot->nvmWrite(BASEBOARDIO_NVM_START_ADDR, data, BASEBOARDIO_NVM_SIZE);

    delete [] data;
    return cmdSuccess;
}
CmdResult BaseboardIO::identifyPortExpanders()
{
    uint8_t addr[8];
    uint8_t result;
    int i;

    // Search Bus
    myLogInfo("Starting OneWire Search");
    enableSwitchedIO();
    for (int j=0;j<10;j++) { // Try 5 times
        i=0;
        mOWMaster.reset();
        mOWMaster.reset_search();
        wait(1.0);
        while (true) {
            // TODO maybe change to family based search
            result = mOWMaster.search(addr);
            if (result != 1) {
                break;
            }
            myLogInfo("ROM Addr: %02x:%02x:%02x:%02x:%02x:%02x:%02x%02x found.",
                    addr[7],addr[6],addr[5],addr[4],addr[3],addr[2],addr[1],addr[0]);
            if (i == 0) {
                std::memcpy(mNvmObj.mPortExpanderROM0, addr, sizeof(mNvmObj.mPortExpanderROM0));
            }
            else if (i == 1) {
                std::memcpy(mNvmObj.mPortExpanderROM1, addr, sizeof(mNvmObj.mPortExpanderROM1));
            }
            i++;
        }
            // TODO maybe only allow a reasonable number of Port Expanders
        if (i >=2) {
            break;
        }
    }

    myLogInfo("Finished OneWire Search");
    if (i != 2) {
        myLogError("Incorrect Number of OneWire devices (Got %d.  Expected 2) OneWire port expanders found.", i);
        return cmdError;
    }

    // All rotary switches should be at 0.  DIPS should be asserted.
    // If switches are set in factory default mode then port expander 1 should read 0xFF and
    // port expander 2 should read 0xF0.

    mPortEx0 = new DS2408(&mOWMaster, mNvmObj.mPortExpanderROM0);
    mPortEx1 = new DS2408(&mOWMaster, mNvmObj.mPortExpanderROM1);


    enableSwitchedIO();
    if (mPortEx0->pioLogicReliableRead(mPortExpanderVal0) != cmdSuccess) {
        myLogError("Error during port expander ID.  Read failed.");
        disableSwitchedIO();
        delete mPortEx0;
        delete mPortEx1;
        return cmdError;
    }
    if (mPortEx1->pioLogicReliableRead(mPortExpanderVal1) != cmdSuccess) {
        myLogError("Error during port expander ID.  Read failed.");
        disableSwitchedIO();
        delete mPortEx0;
        delete mPortEx1;
        return cmdError;
    }

    disableSwitchedIO();
    if ((mPortExpanderVal0 == 0xFF) and (mPortExpanderVal1 == 0xF0)) { // Luckily got it right
        myLogInfo("ROMS Swap Not Needed.");
    }
    else if ((mPortExpanderVal0 == 0xF0) and (mPortExpanderVal1 == 0xFF)) { // Just need to swap
        std::memcpy(addr, mNvmObj.mPortExpanderROM0, sizeof(addr)); // Store Orig ROM0 -> addr
        std::memcpy(mNvmObj.mPortExpanderROM0, mNvmObj.mPortExpanderROM1, sizeof(mNvmObj.mPortExpanderROM0)); // Store Orig ROM1 -> ROM0
        std::memcpy(mNvmObj.mPortExpanderROM1, addr, sizeof(mNvmObj.mPortExpanderROM1)); // Store Orig ROM0 (addr) -> ROM1
        myLogInfo("Swapped ROMS.");
    }
    else {
        myLogError("Error during port expander ID.  Port expanders not in "
                "expected states (0xFF and 0xF0).  Check user switches.  Got %02X and %02X",
                mPortExpanderVal0, mPortExpanderVal1);
        delete mPortEx0;
        delete mPortEx1;
        return cmdError;
    }

    // Cleanup
    delete mPortEx0;
    delete mPortEx1;

    return cmdSuccess;
}
CmdResult BaseboardIO::openRelay() {
    uint8_t val;
    mPortEx1->pioLogicReliableRead(val);

    val |= pEx1RlyA; // Make sure Relay A is off
    val &= ~pEx1RlyB; // Turn on Relay B

    if (mPortEx1->pioLogicReliableWrite(val | ~pEx1OutMask) != cmdSuccess) {
        val |= pEx1RlyA; // Turn Relay A off
        val |= pEx1RlyB; // Turn Relay B off
        mPortEx1->pioLogicReliableWrite(val | ~pEx1OutMask); // Write a non assert value just to try to overcome an error
        myLogError ("Error turning on coil.  Turning both coils off.");
        return cmdError;
    }

    wait(COIL_ON_TIME);

    val |= pEx1RlyA; // Turn Relay A off
    val |= pEx1RlyB; // Turn Relay B off

    if (mPortEx1->pioLogicReliableWrite(val | ~pEx1OutMask) != cmdSuccess) {
        mPortEx1->pioLogicReliableWrite(val | ~pEx1OutMask);
        myLogError ("Error turning off coils.  Trying again.");
        return cmdError;
    }

    return cmdSuccess;
}
CmdResult BaseboardIO::closeRelay() {
    uint8_t val;
    mPortEx1->pioLogicReliableRead(val);

    val &= ~pEx1RlyA; // Turn on Relay A
    val |= pEx1RlyB; // Make sure Relay B is off

    if (mPortEx1->pioLogicReliableWrite(val | ~pEx1OutMask) != cmdSuccess) {
        val |= pEx1RlyA; // Turn Relay A off
        val |= pEx1RlyB; // Turn Relay B off
        mPortEx1->pioLogicReliableWrite(val | ~pEx1OutMask); // Write a non assert value just to try to overcome an error
        myLogError ("Error turning on coil.  Turning both coils off.");
        return cmdError;
    }

    wait(COIL_ON_TIME);

    val |= pEx1RlyA; // Turn Relay A off
    val |= pEx1RlyB; // Turn Relay B off

    if (mPortEx1->pioLogicReliableWrite(val | ~pEx1OutMask) != cmdSuccess) {
        mPortEx1->pioLogicReliableWrite(val | ~pEx1OutMask);
        myLogError ("Error turning off coils.  Trying again.");
        return cmdError;
    }

    return cmdSuccess;
}

CmdResult BaseboardIO::prepareSleep()
{
	// Save current GPUIO state
    xdot_save_gpio_state();

    // Configure all IO expect for pins for interrupt in lowest mode possible
    // GPIO Ports Clock Enable
    __GPIOA_CLK_ENABLE();
    __GPIOB_CLK_ENABLE();
    __GPIOC_CLK_ENABLE();
    __GPIOH_CLK_ENABLE();

    GPIO_InitTypeDef GPIO_InitStruct;

    // UART1_TX, UART1_RTS & UART1_CTS to analog nopull - RX could be a wakeup source
    // UART1_TX, UART1_RTS & UART1_CTS to analog nopull - RX could be a wakeup source
//    GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_11 | GPIO_PIN_12;
    GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    // I2C_SDA & I2C_SCL to analog nopull
    GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    // SPI_MOSI, SPI_MISO, SPI_SCK, & SPI_NSS to analog nopull
    GPIO_InitStruct.Pin = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    // iterate through potential wake pins - leave the configured wake pin alone if one is needed
    if ((CCInPinName != WAKE && TamperPinName != WAKE && PairBtnPinName != WAKE)
            || dot->getWakeMode() == mDot::RTC_ALARM) {
        GPIO_InitStruct.Pin = GPIO_PIN_0;
        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
    if ((CCInPinName != GPIO0 && TamperPinName != GPIO0 && PairBtnPinName != GPIO0)
            || dot->getWakeMode() == mDot::RTC_ALARM) {
        GPIO_InitStruct.Pin = GPIO_PIN_4;
        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
    if ((CCInPinName != GPIO1 && TamperPinName != GPIO1 && PairBtnPinName != GPIO1)
            || dot->getWakeMode() == mDot::RTC_ALARM) {
        GPIO_InitStruct.Pin = GPIO_PIN_5;
        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
    if ((CCInPinName != GPIO2 && TamperPinName != GPIO2 && PairBtnPinName != GPIO2)
            || dot->getWakeMode() == mDot::RTC_ALARM) {
        GPIO_InitStruct.Pin = GPIO_PIN_0;
        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    }
    // Changed to always do pull down for now.. Should implement discrete pull on new rev
//    if ((CCInPinName != GPIO3 && TamperPinName != GPIO3 && PairBtnPinName != GPIO3)
//            || dot->getWakeMode() == mDot::RTC_ALARM) {
//        GPIO_InitStruct.Pin = GPIO_PIN_2;
//        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
//        GPIO_InitStruct.Pull = GPIO_NOPULL;
//        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
//    }

    // Try doing nothing so that the GPIO3 is still and active output


    // Cannot do this and have serial data
//    if ((CCInPinName != UART1_RX && TamperPinName != UART1_RX && PairBtnPinName != UART1_RX)
//            || dot->getWakeMode() == mDot::RTC_ALARM) {
//        GPIO_InitStruct.Pin = GPIO_PIN_10;
//        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
//        GPIO_InitStruct.Pull = GPIO_NOPULL;
//        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
//    }
    if ((CCInPinName != UART_CTS && TamperPinName != UART_CTS && PairBtnPinName != UART_CTS)
            || dot->getWakeMode() == mDot::RTC_ALARM) {
        GPIO_InitStruct.Pin = GPIO_PIN_11;
        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }

    return cmdSuccess;
}

CmdResult BaseboardIO::exitSleep()
{
    xdot_restore_gpio_state();
    return cmdSuccess;
}

// NvmBBIOObj
NvmBBIOObj::NvmBBIOObj()
{
    setDefaults();
}
void NvmBBIOObj::setDefaults()
{
    mBaseboardIOFlag = BASEBOARDIO_FLAG;
    mBaseboardIORev  = BASEBOARDIO_REV;
    mSerialNum = 0x0000;
    mBaseboardIOConfig = 0x0000;
    std::memset(mPortExpanderROM0, 0x00, 8);
    std::memset(mPortExpanderROM1, 0x00, 8);
}
CmdResult NvmBBIOObj::fromBytes(uint8_t *data, uint8_t size)
{
    if (size != BASEBOARDIO_NVM_SIZE) {
        return cmdError;
    }

    mBaseboardIOFlag    = *((uint16_t *) (data));
    mBaseboardIORev     = *((uint16_t *) (data+2));
    mSerialNum          = *((uint32_t *) (data+4));
    mBaseboardIOConfig  = *((uint32_t *) (data+6));

    std::memcpy(&mPortExpanderROM0, data+0x10, 8);
    std::memcpy(&mPortExpanderROM1, data+0x18, 8);

    return cmdSuccess;
}
CmdResult NvmBBIOObj::toBytes(uint8_t *data, uint8_t &size) {
    // TODO check data size

    *((uint16_t *) (data))   = mBaseboardIOFlag;
    *((uint16_t *) (data+2)) = mBaseboardIORev;
    *((uint32_t *) (data+4)) = mSerialNum;
    *((uint32_t *) (data+6)) = mBaseboardIOConfig;

    std::memcpy(data+0x10, &mPortExpanderROM0, 8);
    std::memcpy(data+0x18, &mPortExpanderROM1, 8);

    size = BASEBOARDIO_NVM_SIZE;

    return cmdSuccess;
}
uint16_t NvmBBIOObj::getBaseboardIOFlag()
{
    return mBaseboardIOFlag;
}
bool NvmBBIOObj::validBaseboardIOFlag()
{
    return mBaseboardIOFlag == BASEBOARDIO_FLAG;
}
uint16_t NvmBBIOObj::getBaseboardIORev()
{
    return mBaseboardIORev;
}
bool NvmBBIOObj::validBaseboardIORev()
{
    return mBaseboardIORev == BASEBOARDIO_REV;
}
uint16_t NvmBBIOObj::getSerialNum()
{
    return mSerialNum;
}
void NvmBBIOObj::setSerialNum(uint16_t in)
{
    mSerialNum = in;
}
uint32_t NvmBBIOObj::getBaseboardIOConfig()
{
    return mBaseboardIOConfig;
}
void NvmBBIOObj::setBaseboardIOConfig(uint32_t in)
{
    mBaseboardIOConfig = in;
}
void NvmBBIOObj::getPortExpanderROM0(uint8_t *addr)
{
    std::memcpy(addr, &mPortExpanderROM0, 8);
}
void NvmBBIOObj::setPortExpanderROM0(const uint8_t *addr)
{
    std::memcpy(&mPortExpanderROM0, addr, 8);
}
void NvmBBIOObj::getPortExpanderROM1(uint8_t *addr)
{
    std::memcpy(addr, &mPortExpanderROM1, 8);
}
void NvmBBIOObj::setPortExpanderROM1(const uint8_t *addr)
{
    std::memcpy(&mPortExpanderROM1, addr, 8);
}