The field version of the solarnano grid on the ionQubes

Fork of SolarNanoGridv3 by SONG Project

Hmi/Hmi.cpp

Committer:
defrost
Date:
2016-09-06
Revision:
36:a5620262f296
Parent:
13:de43f28c0365

File content as of revision 36:a5620262f296:

/**
 *@section DESCRIPTION
 * mbed SolarNanogrid  Library
 * Hmi extends SolarNanoGrid.
 * Hmi does the human interface and talks to the lockers.
 * The  ID must be in FF 00.
 *@section LICENSE
 * Copyright (c) 2016, Malcolm McCulloch
 *
 * 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 THE
 * AUTHORS OR COPYRIGHT HOLDERS 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.
 * @file "Hmi.c"
 */
#include <Hmi/Hmi.h>
#define FUNCNAME "HMI"
#include "defs.h"

Hmi::Hmi(FILE* fp, Serial *pc) :
SolarNanoGrid(fp,pc) {
    DBG("Initialize class");
    // No other information needed from the config file
    fclose(fp);

    DBG("Create hardware");
    //
    // Create Hardware
    //
    //
    // Setup NRF
    //
    baseAddr = ((long long) communityID << 16) + (id & 0XFFFF);
    utilityAddr = baseAddr & 0XFFFFFF0000 | 0xFE00;
    //utilityAddr = 0x26c1e10100;
    DBG("  Channel:%x, Address %llx Utility Address %llx", chan, baseAddr, utilityAddr);

    spiNRF();
    nrf->quickTxSetup(chan, utilityAddr);
    nrf->setRadio(0x01, 0x03); // 2MB/S  0dB
    nrf->setTxRetry(0x0F, 0x0F);
    nrfFlush();
    DBG("Nrf Details:");
#ifdef DEBUG
    nrf->printDetails();
#endif
//  spiNRF();
//  sprintf(dataTx,"Testing");
//  DBG("Send :%s",dataTx);
//  nrf->transmitData(dataTx,strlen(dataTx));

    DBG("Create hardware RFID");
    // RFID
    BatteryRfChipH = new MFRC522(PTD2, PTD3, PTD1, PTB2, PTB3);
    PoshoRfChipH = new MFRC522 (PTD2, PTD3, PTD1, PTB10, PTB11);
    IncubatorRfChipH = new MFRC522  (PTD2, PTD3, PTD1, PTC11, PTC10);

    DBG("Create hardware TFT");
    //Screen
    TFT_H = new SPI_TFT_ILI9341(PTD2, PTD3, PTD1, PTE25, PTB20, PTB18,"TFT");     /**< LCD Display */

    DBG("Create hardware Buttons");
    // Buttons
    buttonOneH= new InterruptIn(PTC3);                 //  Button one interrupt
    buttonTwoH = new InterruptIn (PTC2);               //  Button two interrupt
    buttonThreeH = new InterruptIn (PTA2);             //  Button three interrupt
    buttonFourH = new InterruptIn (PTB23);             //  Button four interrupt

    DBG("Create hardware Tickers");
    // Create Timers
    tick1sHub = new Ticker();                           /**< Every second time check */
    tickBatteryRfidH  = new Ticker();                   /**< Battery Rfid check */
    tickIncubatorRfidH  = new Ticker();                 /**< Incubator Rfid check */
    tickPoshoRfidH  = new Ticker();                     /**< Posho Rfid check */
    tickTimeCheckH  = new Ticker();                     /**< Time check for terminal display */
    tickerUpdateUserTableH  = new Ticker();             /**< Update user table to SD card ticker (every 30 min) */



    spiSD();
    DBG("Initialise flags");
    //
    // Initialise flags
    //

    flagBatteryRfidH =0 ;       /**< Battery Rfid flag */
    flagButtonOneH = 0;         /**< Button one flag */
    flagButtonTwoH = 0;         /**< Button two flag */
    flagButtonThreeH = 0;       /**< Button three flag */
    flagButtonFourH = 0;        /**< Button four flag */
    flagPoshoRfidH =0;          /**< Posho Rfid flag */
    flagIncubatorRfidH =0;      /**< Incubator Rfid flag */
    flagTimeCheckH = 0;
    flagUpdateUserTableH = 0;   /**< Update user table to SD card ticker */
    flag1sH = 0;                /**< Every second flag */


    DBG("Initialise variables");
    //
    // Initialise variables
    //

    currentScreenH = initialScanRfid;
    currentMaxH = 1.5;
    userCountH = 0;                 /**< Number of active users on this hub mbed */
    currentUserH=0;                 /**< Index of the current user */

    DBG("Initialise variables Posho");
    poshoPricePerKgH = -1;          /**< price per kg of material to be used with the posho */
    timeForOneKgH = 0;              /**< time for one kg of material to be used with the posho */
    timeForTwoKgH = 0;              /**< time for two kg of material to be used with the posho */
    timeForThreeKgH=0;              /**< time for three kg of material to be used with the posho */

    flagPoshoAlreadyInUseH = 0;

    allUsersH = (HubUser *)calloc(256, sizeof(HubUser));



    //
    // Initialise Hardware
    //

    DBG("Initialise hardware RFID");
    //RFID

    BatteryRfChipH->PCD_Init();
    PoshoRfChipH->PCD_Init();
    IncubatorRfChipH->PCD_Init();


    uint8_t tempA = BatteryRfChipH->PCD_ReadRegister(MFRC522::VersionReg);
    DBG("Battery MFRC522 version: %d\n\r", tempA & 0x07);
    uint8_t tempB = PoshoRfChipH->PCD_ReadRegister(MFRC522::VersionReg);
    DBG("Posho MFRC522 version: %d\n\r", tempB & 0x07);
    uint8_t tempC = IncubatorRfChipH->PCD_ReadRegister(MFRC522::VersionReg);
    DBG("Incubator MFRC522 version: %d\n\r", tempC & 0x07);

    DBG("Initialise hardware TFT");
    // TFT Screen
    TFT_H->background(Black);    // set background to black
    TFT_H->foreground(White);    // set chars to white
    TFT_H->cls();                // clear the screen
    TFT_H->set_font((unsigned char*) Arial24x23);
    TFT_H->set_orientation(3);   //Portrait, wiring on left

    //
    // Attach interrupts
    //
    DBG("Attach interrupts Buttons");
    // Buttons
    buttonOneH->rise(this, &Hmi::interruptButtonOneH);
    buttonTwoH->rise(this, &Hmi::interruptButtonTwoH);
    buttonThreeH->rise(this, &Hmi::interruptButtonThreeH);
    buttonFourH->rise(this, &Hmi::interruptButtonFourH);

    DBG("Attach interrupts Timers");
    //Timers
    tick1sHub->attach(this, &Hmi::int1sH, 10.0);
    tickBatteryRfidH->attach(this, &Hmi::interruptBatteryRfidH, 1.0);
    tickIncubatorRfidH->attach(this, &Hmi::interruptIncubatorRfidH, 1.0);
    tickPoshoRfidH->attach(this, &Hmi::interruptPoshoRfidH, 1.0);
    tickTimeCheckH->attach(this, &Hmi::interruptTimeCheckH, 5.0);
    tickerUpdateUserTableH->attach(this, &Hmi::interruptUpdateUserTableH, 1800.0);


    DBG("Posho ");
    //while (poshoPricePerKgH == -1)
    initializePoshoFunctionalityH();
    DBG("Users ");
    initializeUsersFromSD_H();

    loopHub();
}

/**
 * Creates a new user by defining user attributes.
 * @param rfid - uint32_t corresponding to unique id of the RFID tag
 * @param accountCredit - int32 for user account balance (can be negative)
 * @param locker - int32_t for which locker (originaly 1-4) user is assigned
 * @param batterySubscription -  int32_t max batteries user can get (can be 0)
 * @param batteriesOut - int32_t number of batteries user has out (usually 0 initially)
 * @param pod - int32_t pod associated with the user (originally 0-15) *must be 0 indexed*
 */
void Hmi::createNewUserH(uint32_t rfid, int32_t accountCredit, int32_t locker,
        int32_t batterySubscription, int32_t batteriesOut, int32_t pod, char* name)
{
    // uid: [community byte][community byte][locker byte][podID (1/2 byte) | 0000 (1/2 byte)]
    uint32_t actualUid = ((((uint32_t)communityID << 16) | ((uint32_t)locker << 8)) |
            ((uint32_t)pod << 4));

    allUsersH[userCountH] = HubUser(actualUid, rfid, accountCredit, locker,
            pod, batteriesOut, batterySubscription, name);
    userCountH++;

    DBG("rfid: %u accountCredit: %d", rfid,accountCredit);
    DBG("UserID (hex):%x Name: %s", actualUid,name);
    DBG("locker: %d pod: %d ", locker,pod);
    DBG("batterySubscription: %d batteriesOut: %d", batterySubscription, batteriesOut);

}

/**
 *  Initialize Global User Table Variable with malloc
 *  @param users - int representing how many users you want the system to hold
 */
void Hmi::initializeGlobalUserTableH(int users)
{
    allUsersH = (HubUser *)calloc(users, sizeof(HubUser));
}
/**
 *  Initialize posho functionality
 */
void Hmi::initializePoshoFunctionalityH()
{
    spiSD();
    FILE *fp = fopen("/sd/poshoInit.txt", "r");

    if (fp == NULL) {
        WARN("Posho text file can't be opened");
        return;
    }



    if (fscanf(fp,"%d %*c %*s",&poshoPricePerKgH)!= 1) ERR("Posho: cannot read price");
    if (fscanf(fp,"%d %*c %*s",&timeForOneKgH)!= 1) ERR("Posho: cannot read time");
    if (fscanf(fp,"%d %*c %*s",&timeForTwoKgH)!= 1) ERR("Posho: cannot read time");
    if (fscanf(fp,"%d %*c %*s",&timeForThreeKgH)!= 1) ERR("Posho: cannot read time");
    fclose(fp);

    DBG("Posho values initialized\n\r");
    DBG("Price per kg: %d\n\r", poshoPricePerKgH);
    DBG("Time 1: %d, 2: %d, and 3: %d\n\r", timeForOneKgH, timeForTwoKgH, timeForThreeKgH);
}

/**
 * Initialize system with users from users.txt on SD card
 */
void Hmi::initializeUsersFromSD_H()
{
    spiSD();
    FILE *fp = fopen("/sd/users.txt", "r");
    if (fp == NULL) {

        WARN("User text file can't be opened\n\r");

        return;
    }

    // first line of the user file has the headers - get to the next line
    char line[180];
    if (fgets(line, 100, fp) != NULL) {

        DBG("user.txt: Format of text file:|%s|", line);

    }

    // read a set of six values at a time, corresponding to a line, from the user text file
    uint32_t rfid;
    int32_t accountCredit;
    int32_t locker;
    int32_t batterySubscription;
    int32_t batteriesOut;
    int32_t pod;
    char* name;

    while(fgets(line, 100, fp) != NULL){
        //DBG("Line = [%s]",line);
        name = (char *) calloc(32,1);
        // rfid Credit  lckr    pod maxBatt outBatt Name
        sscanf(line, "%u %d %d %d %d %d %s", &rfid, &accountCredit, &locker, &pod,
                &batterySubscription, &batteriesOut, name);
        createNewUserH(rfid, accountCredit, locker, batterySubscription, batteriesOut, pod,name);
        //| Rfid number (%d) | Community ID (%d) |Locker ID (%d) |Pod ID (%d)| Battery Subscriptton(%d)| Battery Out| Name
    }
    fclose(fp);
}
/*************************************************************************************************/
/* Set a flag when an interrupt is detected */
/*************************************************************************************************/
/**
 *  Time check interrupt
 */
void Hmi::interruptTimeCheckH()
{ flagTimeCheckH = 1; }

/**
 *  Update user table interrupt
 */
void Hmi::interruptUpdateUserTableH()
{ flagUpdateUserTableH = 1; }

/**
 *  Battery RFID reader interrupt
 */
void Hmi::interruptBatteryRfidH()
{ flagBatteryRfidH = 1; }

/**
 *  Posho RFID reader interrupt
 */
void Hmi::interruptPoshoRfidH()
{ flagPoshoRfidH = 1; }

/**
 *  Incubator RFID reader interrupt
 */
void Hmi::interruptIncubatorRfidH()
{ flagIncubatorRfidH = 1; }

/**
 *  buttone one interrupt
 */
void Hmi::interruptButtonOneH()
{ flagButtonOneH = 1; }

/**
 *  buttone two interrupt
 */
void Hmi::interruptButtonTwoH()
{ flagButtonTwoH = 1; }

/**
 *  buttone three interrupt
 */
void Hmi::interruptButtonThreeH()
{ flagButtonThreeH = 1; }

/**
 *  buttone four interrupt
 */
void Hmi::interruptButtonFourH()
{ flagButtonFourH = 1; }

/**
 * Fast interrupt routine for every 1 second
 */
void Hmi::int1sH()
{ flag1sH=1; }

/**
 *  interrupt to clear the posho is in use flag after a designated time
 */
void Hmi::interruptPoshoInUseClearH()
{
    flagPoshoAlreadyInUseH = 0;
    tickPoshoInUseClearH.detach();
}

/*************************************************************************************************/
/*
 *  Reset all user initiated flags. Useful after sequences of events where flags may have
 *  piled up from misc inputs and you don't want the system to act seemingly sporadically.
 */
void Hmi::clearAllUserInitiatedFlagsH()
{
    flagButtonOneH = 0;
    flagButtonTwoH = 0;
    flagButtonThreeH = 0;
    flagButtonFourH = 0;
    flagBatteryRfidH = 0;
    flagPoshoRfidH = 0;
    flagIncubatorRfidH = 0;
}

/*
 * User presses the button corresponding to an exit. Returns to home screen
 * after logging the action.
 * @param userIndex - int representing index of user in user table
 */
void Hmi::cancelPressedH(int userIndex)
{
    clearAllUserInitiatedFlagsH();
    currentScreenH = initialScanRfid;
    logActionWithUserInfo(hubAction_Exit, &allUsersH[userIndex]);
}

/**
 *  Do if time check flag is set
 *  reads the unix time, converts into human readable format, and displays on PC
 */
void Hmi::doTimeCheckH()
{
    flagTimeCheckH = 0;
    time_t seconds = time(NULL);
    INFO("%s\n\r", ctime(&seconds));
//  spiNRF();
//  sprintf(dataTx,"hello 1 1");
//  DBG("Send :%s",dataTx);
//  nrf->transmitData(dataTx,strlen(dataTx));
}

/**
 *  Do if update user table flag is set
 */
void Hmi::doUpdateUserTableH()
{
    DBG("doUpdateUserTableH");

    flagUpdateUserTableH = 0;
    // Write to a users.txt file
    spiSD();
    char * name = "/sd/users.txt";
    FILE *fp;
    fp = fopen(name, "w");
    // output the header for the users.txt file
    spiSD();
    fputs("rfid Credit  lckr    pod maxBatt outBatt Name\n\r", fp);

    // output buffer
    char logLine[180];

    for (int i = 0; i < userCountH; i++) {
        // get all the needed information in strings
        spiSD();
        fprintf(fp, "%u %d %d %d %d %d %s\n\r", allUsersH[i].getRfid(), allUsersH[i].getCredit(),
                allUsersH[i].getLocker(), allUsersH[i].getPod(),
                allUsersH[i].getBatteriesMax(), allUsersH[i].getBatteriesOut(),
                allUsersH[i].getName());
    }
    spiSD();
    fclose(fp);
}

/*************************************************************************************************/
/* RFID support */
/*************************************************************************************************/
/**
 * Read Rfid card and parse out the tag id into a single integer. Log if tag is either known
 * or unknown. Sets the global variable for the current user if a user is found.
 * @param rfidReader - MFRC522* of the RFID reader
 * @param source - HubLoggingRfidSource enum for which RFID reader (battery, posho, incubator)
 * @return int representing the userIndex of the HubUser found by the RFID tag. If no user is
        found, returns a -1.
 */
int Hmi::rfidReadHelper(MFRC522 *rfidReader, enum HubLoggingRfidSource source)
{
    if (!rfidReader->PICC_IsNewCardPresent()) {
        return -1;
    }

    if (!rfidReader->PICC_ReadCardSerial()) {
#ifdef debug
        printf("Card not readable\n\r");
#endif
        return -1;
    }

    // if this is for the posho, is the posho already in use?

    if ((source == source_posho) && flagPoshoAlreadyInUseH) {
        TFT_H->cls();
        TFT_H->locate(0,0);
        TFT_H->printf("Posho in use\n\r");
        wait(1);
        currentScreenH = initialScanRfid;
        return -1;
    }

    // get the id of the scanned tag
    uint8_t tempReadingRfid[4];
    for(uint8_t i = 0; i < rfidReader->uid.size; i++) {
        tempReadingRfid[i] = rfidReader->uid.uidByte[i];
    }

    // concatenate the 4 bytes into a single integer via bit shifting
    uint32_t actualRfid = ((((uint32_t)tempReadingRfid[0] << 24) |
            ((uint32_t)tempReadingRfid[1] << 16)) |
            (((uint32_t)tempReadingRfid[2] << 8) |
                    ((uint32_t)tempReadingRfid[3] << 0)));

#ifdef debug
    printf("User:");
    for(uint8_t i = 0; i < rfidReader->uid.size; i++)
    {
        uint8_t uidByte = rfidReader->uid.uidByte[i];
        printf(" %d", uidByte);
    }
    printf("\n\rUserID: %u\n\r", actualRfid);
#endif

    // find the user info
    char foundUserFlag = 0;
    int foundUserIndex;
    for (int i = 0; i < userCountH; i++) {
        if (allUsersH[i].getRfid() == actualRfid) {
            currentUserH = i;
            foundUserIndex = i;
            foundUserFlag = 1;
            break;
        }
    }

    // if the user isn't found, log that an rfid without a user was used and display home screen
    if (!foundUserFlag) {
#ifdef debug
        printf("User not found\n\r");
        printf("ID:%u\n\r", actualRfid);
#endif
        // log the error interaction
        logErrorUnknownRfidScanned(actualRfid, source);

        // let user know tag wasn't found
        TFT_H->cls();
        TFT_H->locate(0,0);
        TFT_H->printf("User not found\n\r");
        TFT_H->printf("ID:%u\n\r", actualRfid);
        wait(1);
        currentScreenH = initialScanRfid;
        return -1;
    }

    // log user scan
    switch (source) {
    case source_battery:
        logActionWithUserInfo(hubAction_BatteryRfidScanned, &allUsersH[foundUserIndex]);
        break;
    case source_posho:
        logActionWithUserInfo(hubAction_PoshoRfidScanned, &allUsersH[foundUserIndex]);
        break;
    case source_incubator:
        logActionWithUserInfo(hubAction_IncubatorRfidScanned, &allUsersH[foundUserIndex]);
        break;
    }

    return foundUserIndex;
}


/*************************************************************************************************/
/* Battery Services */
/*************************************************************************************************/
/**
 *  Do if Battery RFID flag is set. Read RFID tag and check user status.
 *  If there's a valid battery subscription, display drop off and/or pick up.
 *  Otherwise, returns to initial screen.
 */
void Hmi::doBatteryRfidH()
{
    flagBatteryRfidH = 0;

    // let the RF reader get tag info
    int foundUserIndex = rfidReadHelper(BatteryRfChipH, source_battery);
    if (foundUserIndex == -1) return;

    // Display info about the user - authorized for batteries?
    char authorizationFlag = 0;
    TFT_H->cls();
    TFT_H->locate(0,0);
    TFT_H->printf("UserID: %d\n\r", allUsersH[foundUserIndex].getRfid());
    TFT_H->printf("Name: %s\n\r", allUsersH[foundUserIndex].getName());

    if (allUsersH[foundUserIndex].getBatteriesMax() > 0) {
        authorizationFlag = 1;
    }
    if (authorizationFlag){
        TFT_H->printf("Locker: %u\n\r", allUsersH[foundUserIndex].getLocker());
        TFT_H->printf("Pod: %u\n\r\n\r", allUsersH[foundUserIndex].getPod());
        TFT_H->printf("Balance:%d\n\r", allUsersH[foundUserIndex].getCredit());
    }else{
        TFT_H->printf("\n\rSorry, you do not \n\rhave any batteries\n\ron this system. \n\r");
    }



    // if not authorized for batteries, return to main screen
    wait(1);
    if (!authorizationFlag) {
        wait(2.0);
        currentScreenH = initialScanRfid;
        return;
    }

    // otherwise, ask user if the user wants to pick up or drop off batteries?
    TFT_H->locate(0,160);
    TFT_H->printf("Battery Action?\n\r");
    TFT_H->locate(0,200);

    uint8_t maxBatteries = allUsersH[foundUserIndex].getBatteriesMax();
    uint8_t outBatteries = allUsersH[foundUserIndex].getBatteriesOut();

    if ((maxBatteries - outBatteries) == 0) {   // can only drop off
        TFT_H->printf(" drop           exit");
    } else if (outBatteries == 0) {             // can only pick up
        TFT_H->printf(" pick           exit");
    } else {                                    // can pickup or dropoff
        TFT_H->printf(" pick     drop  exit");
    }

    // go to action selecting screen and clear buttons beforehand
    currentScreenH = batterySelectAction;
    clearAllUserInitiatedFlagsH();
    return;
}

/**
 *  Do if user selects to pickup a battery. Determines how many batteries
 *  a user can pickup based off user info and displays corresponding text
 *  @param userIndex - int representing index of user in user table
 */
void Hmi::batteryPickUpScreenH(int userIndex) {
    TFT_H->cls();
    TFT_H->locate(0,0);
    TFT_H->printf("UserID: %u\n\r\n\r", allUsersH[userIndex].getRfid());

    TFT_H->printf("Action: PICK UP\n\r");
    TFT_H->locate(0,160);
    TFT_H->printf("How many batteries?\n\r");
    TFT_H->locate(0,200);
    switch (allUsersH[userIndex].getBatteriesMax() - allUsersH[userIndex].getBatteriesOut()) {
    case 1: {
        TFT_H->printf("  1            exit");
        break;
    }
    case 2: {
        TFT_H->printf("  1    2       exit");
        break;
    }
    case 3: {
        TFT_H->printf("  1    2    3  exit");
        break;
    }
    }

    // go to wait for selection input and reset button flags
    currentScreenH = batterySelectNumberForPickup;
    clearAllUserInitiatedFlagsH();
    return;
}

/**
 *  Do if user selects to dropoff a battery. Determines how many batteries
 *  a user can pickup based off user info and displays corresponding text
 *  @param userIndex - int representing index of user in user table
 */
void Hmi::batteryDropOffScreenH(int userIndex) {
    TFT_H->cls();
    TFT_H->locate(0,0);
    TFT_H->printf("UserID: %u\n\r\n\r", allUsersH[userIndex].getRfid());

    TFT_H->printf("Action: DROP OFF\n\r");
    TFT_H->locate(0,160);
    TFT_H->printf("How many batteries?\n\r");
    TFT_H->locate(0,200);
    switch (allUsersH[userIndex].getBatteriesOut()) {
    case 1: {
        TFT_H->printf("  1            exit");
        break;
    }
    case 2: {
        TFT_H->printf("  1    2       exit");
        break;
    }
    case 3: {
        TFT_H->printf("  1    2    3  exit");
        break;
    }
    }

    // go to wait for selection input and reset button flags
    currentScreenH = batterySelectNumberForDropoff;
    clearAllUserInitiatedFlagsH();
    return;
}

/**
 *  Do after user selects number of batteries to drop off.
 *  Logs the action, changes user info, transmits instructions to other systems
 *  @param numBatteries - int for number of batteries selected to drop off
 *  @param userIndex - int representing index of user in user table
 */
void Hmi::batteryDropOffH(int numBatteries, int userIndex) {
    switch(numBatteries) {
    case 1:
        logActionWithUserInfo(hubAction_OneBatteryDropped, &allUsersH[userIndex]);
        txBatteryDropOff(userIndex,1);
        allUsersH[userIndex].dropOffBattery(1);
        break;
    case 2:
        logActionWithUserInfo(hubAction_TwoBatteryDropped, &allUsersH[userIndex]);
        txBatteryDropOff(userIndex,2);
        allUsersH[userIndex].dropOffBattery(2);
        break;
    case 3:
        logActionWithUserInfo(hubAction_ThreeBatteryDropped, &allUsersH[userIndex]);
        txBatteryDropOff(userIndex,3);
        allUsersH[userIndex].dropOffBattery(3);
        break;
    }

    currentScreenH = initialScanRfid;
    return;
}

/**
 *  Do after user selects number of batteries to pick up.
 *  Logs the action, changes user info, transmits instructions to other systems
 *  @param numBatteries - int for number of batteries selected to pick up
 *  @param userIndex - int representing index of user in user table
 */
void Hmi::batteryPickUpH(int numBatteries, int userIndex) {
    switch(numBatteries) {
    case 1:
        logActionWithUserInfo(hubAction_OneBatteryPicked, &allUsersH[userIndex]);
        txBatteryPickUp(userIndex,1);
        allUsersH[userIndex].pickUpBattery(1);
        break;
    case 2:
        logActionWithUserInfo(hubAction_TwoBatteryPicked, &allUsersH[userIndex]);
        txBatteryPickUp(userIndex,2);
        allUsersH[userIndex].pickUpBattery(2);
        break;
    case 3:
        logActionWithUserInfo(hubAction_ThreeBatteryPicked, &allUsersH[userIndex]);
        txBatteryPickUp(userIndex,3);
        allUsersH[userIndex].pickUpBattery(3);
        break;
    }

    currentScreenH = initialScanRfid;
    return;
}

/*************************************************************************************************/
/* Misc Services */
/*************************************************************************************************/
/**
 * Do if Posho RFID flag is set. Reads rfid and checks user table for a positive balance
 * capable of paying for the posho. Displays screen info for selecting posho amount to process.
 */
void Hmi::doPoshoRfidH()
{
    flagPoshoRfidH = 0;

    // let the RF reader get tag info
    int foundUserIndex = rfidReadHelper(PoshoRfChipH, source_posho);
    if (foundUserIndex == -1) return;

    // Display info about the user
    char authorizationFlag = 0;
    TFT_H->cls();
    TFT_H->locate(0,0);
    TFT_H->printf("UserID: %u\n\r", allUsersH[foundUserIndex].getRfid());
    int userAccountBalance = allUsersH[foundUserIndex].getCredit();
    TFT_H->printf("Balance:%d\n\r", userAccountBalance);
    if (userAccountBalance > 1*poshoPricePerKgH) {
        authorizationFlag = 1;
    }
    TFT_H->printf("Authorization:%s\n\r", (authorizationFlag)? "YES":"NO");


    // if not authorized for batteries, return to main screen
    wait(1);
    if (!authorizationFlag) {
        currentScreenH = initialScanRfid;
        return;
    }

    // otherwise, ask user how may kg of material to process?
    TFT_H->cls();
    TFT_H->locate(0,0);
    TFT_H->printf("UserID: %u\n\r\n\r", allUsersH[foundUserIndex].getRfid());

    TFT_H->printf("Action: Posho\n\r");
    TFT_H->locate(0,160);
    TFT_H->printf("How many kilos?\n\r");
    TFT_H->locate(0,200);
    if (userAccountBalance > 3*poshoPricePerKgH) {
        TFT_H->printf("  1    2    3  exit");
    } else if (userAccountBalance > 2*poshoPricePerKgH) {
        TFT_H->printf("  1    2       exit");
    } else {
        TFT_H->printf("  1            exit");
    }

    // go to wait for selection input and reset button flags
    currentScreenH = poshoSelectKilograms;
    clearAllUserInitiatedFlagsH();
    return;
}

/**
 *  Prepare to active the posho and husker for serial use. Change user balance as necessary
 *  and send corresponding signal. Also sets posho flag to in use and sets an interrupt
 *  to clear that flag after a specified time.
 *  @param numKilograms - int for number of kilograms to be processed
 *  @param userIndex - int representing index of user in user table
 */
void Hmi::poshoSerialUseH(int numKilograms, int userIndex) {
    flagPoshoAlreadyInUseH = 1;
    allUsersH[userIndex].decreaseCredit(numKilograms*poshoPricePerKgH);
    switch (numKilograms) {
    case 1:
        tickPoshoInUseClearH.attach(this, &Hmi::interruptPoshoInUseClearH, (float)timeForOneKgH);
        logActionWithUserInfo(hubAction_OneKiloPosho, &allUsersH[userIndex]);
        break;
    case 2:
        tickPoshoInUseClearH.attach(this, &Hmi::interruptPoshoInUseClearH, (float)timeForTwoKgH);
        logActionWithUserInfo(hubAction_TwoKiloPosho, &allUsersH[userIndex]);
        break;
    case 3:
        tickPoshoInUseClearH.attach(this, &Hmi::interruptPoshoInUseClearH, (float)timeForThreeKgH);
        logActionWithUserInfo(hubAction_ThreeKiloPosho, &allUsersH[userIndex]);
        break;
    }
    TFT_H->cls();
    TFT_H->locate(0,0);
    TFT_H->printf("UserID: %u\n\r\n\r", allUsersH[userIndex].getRfid());
    TFT_H->printf("New balance: %d\n\r", allUsersH[userIndex].getCredit());
    txPoshoSerialUse(numKilograms);
    wait(1);
    currentScreenH = initialScanRfid;
    return;
}

/**
 *  Do if Incubator RFID flag is set. Add incubator functionality later.
 */
void Hmi::doIncubatorRfidH()
{
    flagIncubatorRfidH = 0;
}

/*************************************************************************************************/
/* Public Methods */
/*************************************************************************************************/
void Hmi::loop(){
    loopHub();
}

void Hmi::loopHub(){
    while (true) {
        // put interrupts here that should supercede anything else
        //if (flag1sH) { do1sH(); }

        // put interrupts here that may be active depending on screen stage
        switch(currentScreenH) {
        case initialScanRfid: {
            TFT_H->cls();
            TFT_H->locate(0,0);
            TFT_H->printf("Please scan your ID");
            currentScreenH = waitForRfid;
            clearAllUserInitiatedFlagsH();
        }
        case waitForRfid: {
            if (flagTimeCheckH) doTimeCheckH();
            if (flagBatteryRfidH) doBatteryRfidH();
            if (flagPoshoRfidH) doPoshoRfidH();
            if (flagIncubatorRfidH) doIncubatorRfidH();
            if (flagUpdateUserTableH) doUpdateUserTableH();
            break;
        }
        case batterySelectAction: {
            uint8_t maxBatteries = allUsersH[currentUserH].getBatteriesMax();
            uint8_t outBatteries = allUsersH[currentUserH].getBatteriesOut();

            if ((maxBatteries - outBatteries) == 0) {
                if (flagButtonOneH) batteryDropOffScreenH(currentUserH);
                if (flagButtonFourH) cancelPressedH(currentUserH);
            } else if (outBatteries == 0) {
                if (flagButtonOneH) batteryPickUpScreenH(currentUserH);
                if (flagButtonFourH) cancelPressedH(currentUserH);
            } else {
                if (flagButtonOneH) batteryPickUpScreenH(currentUserH);
                if (flagButtonThreeH) batteryDropOffScreenH(currentUserH);
                if (flagButtonFourH) cancelPressedH(currentUserH);
            }

            break;
        }
        case batterySelectNumberForDropoff: {
            switch (allUsersH[currentUserH].getBatteriesOut()) {
            case 1: {
                if (flagButtonOneH) batteryDropOffH(1, currentUserH);
                if (flagButtonFourH) cancelPressedH(currentUserH);
                break;
            }
            case 2: {
                if (flagButtonOneH) batteryDropOffH(1, currentUserH);
                if (flagButtonTwoH) batteryDropOffH(2, currentUserH);
                if (flagButtonFourH) cancelPressedH(currentUserH);
                break;
            }
            case 3: {
                if (flagButtonOneH) batteryDropOffH(1, currentUserH);
                if (flagButtonTwoH) batteryDropOffH(2, currentUserH);
                if (flagButtonThreeH) batteryDropOffH(3, currentUserH);
                if (flagButtonFourH) cancelPressedH(currentUserH);
                break;
            }
            }
            break;
        }
        case batterySelectNumberForPickup: {
            uint8_t maxBatteries = allUsersH[currentUserH].getBatteriesMax();
            uint8_t outBatteries = allUsersH[currentUserH].getBatteriesOut();

            switch (maxBatteries - outBatteries) {
            case 1: {
                if (flagButtonOneH) batteryPickUpH(1, currentUserH);
                if (flagButtonFourH) cancelPressedH(currentUserH);
                break;
            }
            case 2: {
                if (flagButtonOneH) batteryPickUpH(1, currentUserH);
                if (flagButtonTwoH) batteryPickUpH(2, currentUserH);
                if (flagButtonFourH) cancelPressedH(currentUserH);
                break;
            }
            case 3: {
                if (flagButtonOneH) batteryPickUpH(1, currentUserH);
                if (flagButtonTwoH) batteryPickUpH(2, currentUserH);
                if (flagButtonThreeH) batteryPickUpH(3, currentUserH);
                if (flagButtonFourH) cancelPressedH(currentUserH);
                break;
            }
            }
            break;
        }
        case poshoSelectKilograms: {
            if (allUsersH[currentUserH].getCredit() > 3*poshoPricePerKgH) {
                if (flagButtonOneH) poshoSerialUseH(1, currentUserH);
                if (flagButtonTwoH) poshoSerialUseH(2, currentUserH);
                if (flagButtonThreeH) poshoSerialUseH(3, currentUserH);
                if (flagButtonFourH) cancelPressedH(currentUserH);
            } else if (allUsersH[currentUserH].getCredit() > 2*poshoPricePerKgH) {
                if (flagButtonOneH) poshoSerialUseH(1, currentUserH);
                if (flagButtonTwoH) poshoSerialUseH(2, currentUserH);
                if (flagButtonFourH) cancelPressedH(currentUserH);
            } else {
                if (flagButtonOneH) poshoSerialUseH(1, currentUserH);
                if (flagButtonFourH) cancelPressedH(currentUserH);
            }
        }
        }
    }
}

void Hmi::logActionWithUserInfo(enum HubActionForLogging action, HubUser *user) {
    // Append to a log text file
    spiSD();
    char * name = "/sd/HubLog.txt";
    FILE *fp;
    fp = fopen(name, "a");
    if (fp == NULL) {
#ifdef debug
        printf("Log text file can't be opened (Action log)\n\r");
#endif
        return;
    }

    // get the time and append it to an output buffer
    time_t seconds = time(NULL);
    char logLine[180];
    sprintf(logLine, "%x", seconds);

    // append relevant information for action
    switch (action) {
    case hubAction_BatteryRfidScanned:
        strcat(logLine, " RB ");
        break;
    case hubAction_PoshoRfidScanned:
        strcat(logLine, " RP ");
        break;
    case hubAction_IncubatorRfidScanned:
        strcat(logLine, " RI ");
        break;
    case hubAction_Exit:
        strcat(logLine, " X ");
        break;
    case hubAction_OneBatteryDropped:
        strcat(logLine, " BD 1 ");
        break;
    case hubAction_TwoBatteryDropped:
        strcat(logLine, " BD 2 ");
        break;
    case hubAction_ThreeBatteryDropped:
        strcat(logLine, " BD 3 ");
        break;
    case hubAction_OneBatteryPicked:
        strcat(logLine, " BP 1 ");
        break;
    case hubAction_TwoBatteryPicked:
        strcat(logLine, " BP 2 ");
        break;
    case hubAction_ThreeBatteryPicked:
        strcat(logLine, " BP 3 ");
        break;
    case hubAction_OneKiloPosho:
        strcat(logLine, " PO 1 ");
        break;
    case hubAction_TwoKiloPosho:
        strcat(logLine, " PO 2 ");
        break;
    case hubAction_ThreeKiloPosho:
        strcat(logLine, " PO 3 ");
        break;
    }

    // append general user information
    char rfidBuffer[20];
    char uidBuffer[20];
    char acctBalanceBuffer[20];
    char maxBatteriesBuffer[20];

    sprintf(rfidBuffer, "%u ", user->getRfid());
    sprintf(uidBuffer, "%u ", user->getUid());
    sprintf(acctBalanceBuffer, "%d ", user->getCredit());
    sprintf(maxBatteriesBuffer, "%d\n", user->getBatteriesMax());

    strcat(logLine, rfidBuffer);
    strcat(logLine, uidBuffer);
    strcat(logLine, acctBalanceBuffer);
    strcat(logLine, maxBatteriesBuffer);

    // write the line to the log file and close the file
    fputs(logLine, fp);
    fclose(fp);

#ifdef debug
    printf("%s\n\r", logLine);
#endif

}

void Hmi::logErrorUnknownRfidScanned(uint32_t unknownRfid, enum HubLoggingRfidSource source) {
    // Append to a log text file
    char * name = "/sd/HubLog.txt";
    spiSD();
    FILE *fp;
    fp = fopen(name, "a");

    if (fp == NULL) {
#ifdef debug
        printf("Log text file can't be opened (Unknown rfid)\n\r");
#endif
        return;
    }

    // get the time and append it to an output buffer
    time_t seconds = time(NULL);
    char logLine[180];
    sprintf(logLine, "%x", seconds);

    // RFID action
    switch (source) {
    case source_battery:
        strcat(logLine, " R X B ");
        break;
    case source_posho:
        strcat(logLine, " R X P ");
        break;
    case source_incubator:
        strcat(logLine, " R X I ");
        break;
    }

    // include just the RFID (indicates that no known user was found)
    char rfidBuffer[20];
    sprintf(rfidBuffer, "%u\n", unknownRfid);
    strcat(logLine, rfidBuffer);

    // write the line to the log file and close the file
    fputs(logLine, fp);
    fclose(fp);

#ifdef debug
    printf("%s\n\r", logLine);
#endif

}

/**
 * Sends a signal to a locker that the current user is picking up numBatteries batteries
 * @param numBatteries - int representing number of batteries user is picking up
 */
void Hmi::txBatteryPickUp(int userIndex,int numBatteries) {
    DBG("txBatteryPickup:");
    int locker = (allUsersH[userIndex].getUid())&0x0000ff00;
    lockerAddr = (baseAddr &0xffffff0000)+locker;
    DBG("txBatteryDropOff: %x base %x lock %x", allUsersH[userIndex].getUid(),(unsigned int) baseAddr,(unsigned int) lockerAddr);
    spiNRF();
    nrf->flushRx();
    nrf->flushTx();
    nrf->setTxAddress(lockerAddr);
#ifdef DEBUG
    nrf->printDetails();
#endif
    sprintf(dataTx,"bp %x %x",allUsersH[userIndex].getUid(), numBatteries);
    DBG("\r\n txBatteryPickup: %s",dataTx);
    nrf->transmitData(dataTx,strlen(dataTx));
    spiSD();

}

/**
 * Sends a signal to a locker that the current user is dropping off numBatteries batteries
 * @param numBatteries - int representing number of batteries user is dropping off
 */
void Hmi::txBatteryDropOff(int userIndex,int numBatteries) {
    DBG("txBatteryDropOff:");
    int locker = (allUsersH[userIndex].getUid())&0x0000ff00;
    lockerAddr = (baseAddr &0xffffff0000)+locker;
    DBG("txBatteryDropOff: %x base %x lock %x", allUsersH[userIndex].getUid(),(unsigned int) baseAddr,(unsigned int) lockerAddr);
    spiNRF();
    nrf->flushRx();
    nrf->flushTx();
    nrf->setTxAddress(lockerAddr);
#ifdef DEBUG
    nrf->printDetails();
#endif
    sprintf(dataTx,"bd %x %x",allUsersH[userIndex].getUid(), numBatteries);
    DBG("\r\n txBatteryDropOff: %s",dataTx);
    nrf->transmitData(dataTx,strlen(dataTx));
    spiSD();
}

/**
 * Sends a signal to a services mbed to schedule the posho/husker for use for x kilograms
 * @param numKilograms - int representing number of kilograms user is processing
 */
void Hmi::txPoshoSerialUse(int numKilograms) {

}