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/SerialTermMgr.cpp

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

File content as of revision 100:0882cf295f8e:

/*
 * SerialTermMgr.cpp
 *
 *  Created on: May 8, 2017
 *      Author: mbriggs
 */

#include "SerialTermMgr.h"
#include "UserInterface.h"

extern Serial pc;
const char ACK = 0x06;
const char NAK = 0x15;
const char SOH = 0x01;
const char EOT = 0x04;
const char SUB = 0x1A;

SerialTermMgr::SerialTermMgr(BaseboardIO *bbio, WinbondSPIFlash *flash, float fwVersion)
{
    mPc = NULL;
    mFwVersion = fwVersion;
    mCurrScreen = mainScreenId;
    mBbio = bbio;
    mFlash = flash;
}
void SerialTermMgr::printScreen()
{
    switch (mCurrScreen) {
    case mainScreenId:
        printMainScreen();
        break;
    case genInfoScreenId:
        printGenInfo();
        break;
    case settingsScreenId:
    	printSettings();
    	break;
    case statsScreenId:
    case errorLogScreenId:
    case liveLogScreenId:
    case enterSerialBridgeScreenId:
        printMainScreen();
        break;
    case enterProgModeScreenId:
        printEnterProgMode();
    }
}
bool SerialTermMgr::input()
{
    if (mPc == NULL) {
        return true;
    }
    bool quit = false;
    char c = 10;
    if (mPc->readable()) {
        c = mPc->getc();
    }
    else {
        return false;  // Do nothing if there is no input
    }
    switch (mCurrScreen) {
    case mainScreenId:
        quit = inputMainPage(c);
        break;
    case genInfoScreenId:
        inputGenInfo(c);
        break;
    case settingsScreenId:
    	inputSettings(c);
    	break;
    case statsScreenId:
    case errorLogScreenId:
    case liveLogScreenId:
    case enterSerialBridgeScreenId:
        inputMainPage(c);
        break;
    case enterProgModeScreenId:
        inputEnterProgMode(c);
    }
    return quit;
}

// private methods
bool SerialTermMgr::inputMainPage (char in) {
    bool quit = false;
    switch (in) {
    case '1':
        mCurrScreen = genInfoScreenId;
        break;
    case '2':
        mCurrScreen = enterProgModeScreenId;
        break;
    case '3':
    	seedSaveSettings();
        mCurrScreen = settingsScreenId;
        break;
    // Future
//    case '4':
//        mPc->printf("\r\nNot implemented yet.\r\n");
////        mCurrScreen = statsScreenId;
//        break;
//    case '5':
//        mPc->printf("\r\nNot implemented yet.\r\n");
////        mCurrScreen = errorLogScreenId;
//        break;
//    case '6':
//        mPc->printf("\r\nNot implemented yet.\r\n");
////        mCurrScreen = liveLogScreenId;
//        break;
//    case '7':
//        mPc->printf("\r\nNot implemented yet.\r\n");
////        mCurrScreen = enterSerialBridgeScreenId;
//        break;
    case 0x11: // ctrl-q
        quit=true;
        break;
    default:
        mCurrScreen = mainScreenId;
    }
    if (!quit) {
        printScreen();
    }
    return quit;
}

void SerialTermMgr::printMainScreen()
{
    if (mPc == NULL) {
        return;
    }
    mPc->printf("\r\n\r\n");
    mPc->printf("===============================================\r\n");
    mPc->printf("= Wireless Bridge Terminal (Firmware: v%0.2f)  =\r\n", mFwVersion);
    mPc->printf("===============================================\r\n");
    mPc->printf("===============================================\r\n");
    mPc->printf("= Selection Options                           =\r\n");
    mPc->printf("===============================================\r\n");
    mPc->printf("= 0: Refresh (Enter and space also work)      =\r\n");
    mPc->printf("= 1: General information                      =\r\n");
    mPc->printf("= 2: Enter programming mode                   =\r\n");
    mPc->printf("= 3: Settings                                 =\r\n");
    // Future
//    mPc->printf("= 4: Statistics                               =\r\n");
//    mPc->printf("= 5: Error log                                =\r\n");
//    mPc->printf("= 6: Live log                                 =\r\n");
//    mPc->printf("= 7: Enter serial bridge mode                 =\r\n");
    mPc->printf("= <ctrl>-q: Exit terminal                     =\r\n");
    mPc->printf("===============================================\r\n");
}

void SerialTermMgr::inputGenInfo (char in) {
    if (mPc == NULL) {
        return;
    }
    switch (in) {
    case 0x1B: // esc
        mCurrScreen = mainScreenId;
        break;
    case 'r':
        mBbio->sampleUserSwitches();
        mPc->printf("User switches sampled.\r\n");
        break;
    default:
        mPc->printf("Invalid key.\r\n");
    }
    printScreen();
}

void SerialTermMgr::printGenInfo()
{
    if (mPc == NULL) {
        return;
    }
    mPc->printf("\r\n\r\n");
    mPc->printf("===============================================\r\n");
    mPc->printf("= General Info (ESC to return to main menu)   =\r\n");
    mPc->printf("===============================================\r\n");
    mPc->printf("= Press r to sample user switch values        =\r\n");
    mPc->printf("===============================================\r\n");
    mPc->printf("===============================================\r\n");
    mPc->printf("= Firmware version: v%0.2f                     =\r\n", mFwVersion);
    //TODO
//    mPc->printf("= Radio EUI: %s =\r\n", mts::Text::bin2hexString(dot->getDeviceId()).c_str());
    mPc->printf("= Contact closure: %s            =\r\n", mBbio->isCCNO() ? "Normally open  " :
                                                                            "Normally closed");
    mPc->printf("= WB Mode: %s                        =\r\n", mBbio->isTx() ? "Transmitter" :
                                                                              "Receiver   ");
//    mPc->printf("= Comm Mode: %s                     =\r\n", mBbio->isLoRaWANMode() ? "LoRaWAN     " :
//                                                                                      "Peer-to-peer");
//    mPc->printf("= Serial Mode: %s                       =\r\n", mBbio->isSerialEnabled() ? "Enabled " :
//                                                                                            "Disabled");
    if (mBbio->isTx()) {
        mPc->printf("= Currently no rotary switches apply for TX   =\r\n");
    }
    else {
        mPc->printf("= Rotary 1 hold setting is %05.1f seconds      =\r\n",
        			HoldTimeSetting::rotVal2Sec(mBbio->rotarySwitch1()));
        mPc->printf("= Rotary 2 currently does not apply for RX    =\r\n");
    }
    mPc->printf("===============================================\r\n");
}

void SerialTermMgr::inputSettings (char in) {
    if (mPc == NULL) {
        return;
    }
    switch (in) {
    case 0x1B: // esc
        mCurrScreen = mainScreenId;
        break;
    case 0x13: // ctrl-s
    	applySaveSettings();
    	mPc->printf("Settings saved \r\n");
        mCurrScreen = mainScreenId;
        break;
    case 'r':
        mBbio->sampleUserSwitches();
        mPc->printf("User switches sampled.\r\n");
        break;
    case '1':
    	mSaveIsCCNO = !mSaveIsCCNO;
    	break;
    case '2':
    	mSaveIsTx = !mSaveIsTx;
    	break;
    case '3':
    	if (!mSaveIsTx) {
    		mSaveRot1 = (++mSaveRot1) % 10;
    	}
    	break;
    default:
        mPc->printf("Invalid key.\r\n");
    }
    printScreen();
}

void SerialTermMgr::printSettings()
{
    if (mPc == NULL) {
        return;
    }
    mPc->printf("\r\n\r\n");
    mPc->printf("===============================================\r\n");
    mPc->printf("= Settings (ESC to return to main menu)       =\r\n");
    mPc->printf("===============================================\r\n");
    mPc->printf("= Press r to sample user switch values        =\r\n");
    mPc->printf("= Press <ctrl>-s to save values               =\r\n");
    mPc->printf("===============================================\r\n");
    mPc->printf("===============================================\r\n");
    mPc->printf("= Setting [Mod Key]: curr value, save value   =\r\n");
    mPc->printf("= Contact closure [1]: %s, %s                 =\r\n",
    		mBbio->isCCNO() ? "NO" : "NC",
    		mSaveIsCCNO ? "NO" : "NC");
    mPc->printf("= WB Mode [2]: %s, %s                         =\r\n",
    		mBbio->isTx() ? "TX" : "RX",
    		mSaveIsTx ? "TX" : "RX");
    if (mSaveIsTx) {
        mPc->printf("= Currently no rotary switches apply for TX   =\r\n");
    }
    else {
        mPc->printf("= Rotary 1 hold setting [3]: %05.1fs, %05.1fs   =\r\n",
        		HoldTimeSetting::rotVal2Sec(mBbio->rotarySwitch1()),
        		HoldTimeSetting::rotVal2Sec(mSaveRot1));
        mPc->printf("= Rotary 2 currently does not apply for RX    =\r\n");
    }
    mPc->printf("===============================================\r\n");
}

void SerialTermMgr::seedSaveSettings()
{
	mSaveIsCCNO = mBbio->isCCNO();
	mSaveIsTx = mBbio->isTx();
	mSaveRot1 = mBbio->rotarySwitch1();
}

void SerialTermMgr::applySaveSettings()
{
	mBbio->setIsCCNO(mSaveIsCCNO);
	mBbio->setIsTx(mSaveIsTx);
	mBbio->setRotarySwitch1(mSaveRot1);
}

void SerialTermMgr::printEnterProgMode()
{
    if (mPc == NULL) {
        return;
    }
    mPc->printf("\r\n\r\n");
    mPc->printf("===============================================\r\n");
    mPc->printf("= Serial Firmware Prog Mode (Esc to return)   =\r\n");
    mPc->printf("===============================================\r\n");
    mPc->printf("===============================================\r\n");
    mPc->printf("= To program firmware:                         \r\n");
    mPc->printf("= 1: Have bin file ready                      =\r\n");
    mPc->printf("= 2: Hit <ctrl>-d to enter XMODEM mode        =\r\n");
    mPc->printf("= 3: In terminal program (such as teraterm)   =\r\n");
    mPc->printf("=    select transfer via XMODEM               =\r\n");
    mPc->printf("= 4: Programming may take up to a minute      =\r\n");
    mPc->printf("= 5: If successful terminal prints \"Success\"  =\r\n");
    mPc->printf("= 6: Wireless Bridge will then automatically  =\r\n");
    mPc->printf("=    reboot with new firmware.                =\r\n");
    mPc->printf("===============================================\r\n");
}

void SerialTermMgr::inputEnterProgMode (char in) {
    if (mPc == NULL) {
        return;
    }
    switch (in) {
    case 0x1B: // esc:
        mCurrScreen = mainScreenId;
        break;
    case 0x04:
        xmodem2Flash();
        break;
    default:
        mPc->printf("Invalid key.\r\n");
    }
    printScreen();
}

bool SerialTermMgr::xmodem2Flash ()
{
    mPc->printf("Ready to receive file\r\n");
    mFlash->releaseFromPowerDown();
    mFlash->chipErase();
    char packetBuf[132];
    uint16_t packetNum = 1;
    uint8_t csum = 0;
    unsigned char pktIdx = 0;
    time_t lastValidInput = 0;
    unsigned char nTimeouts = 0;
    while (1) {
        // Send a NAK if a packet is not received within timeout period
        if ((time(NULL)-lastValidInput) > XMODEM_TIMEOUT){
            pc.printf("Total chars: %d\r\n", pktIdx);
            pktIdx = 0;
            mPc->printf("%c", NAK); // Send NAK
            lastValidInput = time(NULL);
            nTimeouts++;
            if (nTimeouts >= MAX_TIMEOUTS) {
                mPc->printf("Programming timed out.\r\n");
                pc.printf("Programming timed out.\r\n");
                break;
            }
        }
        if (mPc->readable()) {
            packetBuf[pktIdx++] = mPc->getc();
            if (pktIdx == 1 && packetBuf[0] == EOT) {
                lastValidInput = time(NULL);
                mPc->printf("%c", ACK); // Last ACK

				// Check for update string in last packet
                bool foundKey;
                for (int packetIdx=3;packetIdx<sizeof(packetBuf)-sizeof(VORTEX_UPDATE_KEY);packetIdx++) {
                	foundKey = true;
					for (int i=0;i<sizeof(VORTEX_UPDATE_KEY)-1;i++) {
						if (packetBuf[packetIdx+i] != VORTEX_UPDATE_KEY[i]) {
							foundKey = false;
							break;
						}
					}
					if (foundKey) {
						break;
					}
                }
                if (foundKey) {
					mPc->printf("Success.\r\n");
					pc.printf("Success on xmodem.  Reset in progress.\r\n");
					writeBootloaderCtrlPage(packetNum*XMODEM_PACKET_SIZE);
                }
                else {
					mPc->printf("Failed update key validation.\r\n");
					pc.printf("Failed update key validation\r\n");
                }

                wait(0.01);
				mFlash->powerDown();
				if (foundKey) {
					NVIC_SystemReset();
				}
                break;
            }
            else if (pktIdx >= 132) {
                lastValidInput = time(NULL);
                nTimeouts = 0; // Clear n timeouts with new packet
                csum = 0;
                for (int i=3; i<131; i++) {
                    csum += packetBuf[i];
                }
                pktIdx = 0;
                if ((csum == packetBuf[131]) && (packetBuf[0] == SOH) &&
                  (packetBuf[1] == ((uint8_t)packetNum)) && (((uint8_t)~packetBuf[2]) == ((uint8_t)packetNum))) {
					mFlash->writeStream(FLASH_BIN_OFFSET+(packetNum-1)*XMODEM_PACKET_SIZE, packetBuf+3,
							XMODEM_PACKET_SIZE);
                    mPc->printf("%c", ACK);
                    packetNum++;
                }
                else {
                    mPc->printf("%c", NAK);
                }
            }
        }
    }
    mCurrScreen = mainScreenId; // Just return to main screen for now
    mFlash->powerDown();

    return true;
}

void SerialTermMgr::writeBootloaderCtrlPage(uint32_t nBytes)
{
	// This should always be less than 256 bytes

	// Zero updates the bootloader version so that is cleared until the bootloader adds its back
	const uint32_t zeroBootloaderVersion = 0;
	const unsigned int ctrlSize = sizeof(NEW_CODE)+sizeof(nBytes)+sizeof(zeroBootloaderVersion);
	char buf[ctrlSize];
	std::memcpy(buf, NEW_CODE, sizeof(NEW_CODE));
	std::memcpy(buf+sizeof(NEW_CODE), &nBytes, sizeof(nBytes));
	std::memcpy(buf+sizeof(NEW_CODE)+sizeof(nBytes),
				&zeroBootloaderVersion, sizeof(zeroBootloaderVersion));
	// TODO add reprogram counter or other functions

	// Copy to first page of flash
    mFlash->writeStream(0, buf, sizeof(buf));
}