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-13
Revision:
78:43f074baac34
Parent:
77:176e3eb8f712
Child:
91:8196900df6fe

File content as of revision 78:43f074baac34:

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

#include "SerialTermMgr.h"
#include "UserInterface.h"
#include "bootloader.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:
    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:
    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':
        mPc->printf("\r\nNot implemented yet.\r\n");
//        mCurrScreen = settingsScreenId;
        break;
    case '3':
        mPc->printf("\r\nNot implemented yet.\r\n");
//        mCurrScreen = statsScreenId;
        break;
    case '4':
        mPc->printf("\r\nNot implemented yet.\r\n");
//        mCurrScreen = errorLogScreenId;
        break;
    case '5':
        mPc->printf("\r\nNot implemented yet.\r\n");
//        mCurrScreen = liveLogScreenId;
        break;
    case '6':
        mPc->printf("\r\nNot implemented yet.\r\n");
//        mCurrScreen = enterSerialBridgeScreenId;
        break;
    case '7':
        mCurrScreen = enterProgModeScreenId;
        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: Settings                                 =\r\n");
    mPc->printf("= 3: Statistics                               =\r\n");
    mPc->printf("= 4: Error log                                =\r\n");
    mPc->printf("= 5: Live log                                 =\r\n");
    mPc->printf("= 6: Enter serial bridge mode                 =\r\n");
    mPc->printf("= 7: Enter programming 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:
        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", mFwVersion);
    mPc->printf("===============================================\r\n");
    mPc->printf("= Press r to sample user switch values        =\r\n"); // FIXME
    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 %0.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::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 will print \"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:
        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
                mPc->printf("Success.\r\n");
                pc.printf("Success on xmodem.  Reset in progress.\r\n");
                writeBootloaderCtrlPage(packetNum*XMODEM_PACKET_SIZE);
//				copyNewProgram();

                wait(0.01);
				mFlash->powerDown();
                NVIC_SystemReset();
				// Should never get here because of reset.
                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
	const unsigned int ctrlSize = sizeof(NEW_CODE)+sizeof(nBytes);
	char buf[ctrlSize];
	std::memcpy(buf, NEW_CODE, sizeof(NEW_CODE));
	std::memcpy(buf+sizeof(NEW_CODE), &nBytes, sizeof(nBytes));
	// TODO add reprogram counter just for fun

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