All mbed code for control over dive planes, pump motor, valve motor, BCUs, UART interface, etc.
Dependencies: mbed ESC mbed MODDMA
Diff: robotic_fish_6/FishController.cpp
- Revision:
- 0:c3a329a5b05d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/robotic_fish_6/FishController.cpp Tue Jan 14 19:17:05 2020 +0000 @@ -0,0 +1,488 @@ + +#include "FishController.h" + +// The static instance +FishController fishController; + +// Function to reset mbed +extern "C" void mbed_reset(); + +// Auto mode +float autoModeCommands[][4] = {FISH_STRAIGHT, FISH_UP, FISH_STRAIGHT, FISH_DOWN}; +uint32_t autoModeDurations[] = {4000, 2000, 2000, 2000}; // durations in milliseconds (is it really? -> check if statement that compares autoModeDurations elements to 10*indexcount +const uint8_t autoModeLength = sizeof(autoModeDurations)/sizeof(autoModeDurations[0]); + +//============================================ +// Initialization +//============================================ + +// Constructor +FishController::FishController(): + // Initialize variables + autoMode(false), + ignoreExternalCommands(false), + tickerInterval(fishControllerTickerInterval), + inTickerCallback(false), + servoLeft(servoLeftPin), + servoRight(servoRightPin), + #ifdef FISH4 + curTime(0), + fullCycle(true), + raiser(3.5), + // Outputs for motor and servos + //motorPWM(motorPWMPin), + //motorOutA(motorOutAPin), + //motorOutB(motorOutBPin), + servoLeft(servoLeftPin), + servoRight(servoRightPin), + //brushlessMotor(p25), + brushlessOffTime(30000), + #endif +/* #ifdef FISH6 // these are declared in BCU class + pressureSensor(pressureSensorPinSDA, pressureSensorPinSCL, ms5837_addr_no_CS), + imuSensor(imuSensorPinSDA, imuSensorPinSCL) + #endif*/ + // Button board + buttonBoard(buttonBoardSDAPin, buttonBoardSCLPin, buttonBoardInt1Pin, buttonBoardInt2Pin) // sda, scl, int1, int2 + +{ + streamFishStateEventController = 0; + + newSelectButton = resetSelectButtonValue; + newPitch = resetPitchValue; + newYaw = resetYawValue; + newThrust = resetThrustValue; + newFrequency = resetFrequencyValue; + newPeriodHalf = resetPeriodHalfValue; + + selectButton = newSelectButton; + pitch = newPitch; + yaw = newYaw; + thrust = newThrust; + frequency = newFrequency; +#ifdef FISH4 + periodHalf = newPeriodHalf; + thrustCommand = 0; + dutyCycle = 0; + brushlessOff = false; +#endif + + buttonBoard.registerCallback(&FishController::buttonCallback); + buttonBoard.setLEDs(255, false); + + autoModeIndex = 0; + autoModeCount = 0; + +} + +// Set the desired state +// They will take affect at the next appropriate time in the control cycle +void FishController::setSelectButton(bool newSelectButtonValue, bool master /* = false*/) +{ + if(!ignoreExternalCommands || master) + newSelectButton = newSelectButtonValue; +} +void FishController::setPitch(float newPitchValue, bool master /* = false*/) +{ + if(!ignoreExternalCommands || master) + { + newPitch = newPitchValue; + setLEDs(BTTN_PITCH_UP, (newPitch-fishMinPitch) > (fishMaxPitch - newPitch)); + setLEDs(BTTN_PITCH_DOWN, (newPitch-fishMinPitch) < (fishMaxPitch - newPitch)); + } +} +void FishController::setYaw(float newYawValue, bool master /* = false*/) +{ + if(!ignoreExternalCommands || master) + { + newYaw = newYawValue; + setLEDs(BTTN_YAW_LEFT, (newYaw-fishMinYaw) < (fishMaxYaw - newYaw)); + setLEDs(BTTN_YAW_RIGHT, (newYaw-fishMinYaw) > (fishMaxYaw - newYaw)); + } +} +void FishController::setThrust(float newThrustValue, bool master /* = false*/) +{ + if(!ignoreExternalCommands || master) + { + newThrust = newThrustValue; + setLEDs(BTTN_FASTER, newThrust>fishMinThrust); + // If we're in button-control mode, keep the no-thrust light on as an indicator + if(!ignoreExternalCommands) + setLEDs(BTTN_SLOWER, newThrust==fishMinThrust); + else + setLEDs(BTTN_SLOWER, true); + } +} +void FishController::setFrequency(float newFrequencyValue, float newPeriodHalfValue /* = -1 */, bool master /* = false*/) +{ + if(!ignoreExternalCommands || master) + { + newFrequency = newFrequencyValue; + newPeriodHalf = newPeriodHalfValue > -1 ? newPeriodHalfValue : (1.0/(2.0*newFrequency)); + } +} +// Get the (possible pending) state +bool FishController::getSelectButton() {return newSelectButton;} +float FishController::getPitch() {return newPitch;} +float FishController::getYaw() {return newYaw;} +float FishController::getThrust() {return newThrust;} +float FishController::getFrequency() {return newFrequency;} +float FishController::getPeriodHalf() {return newPeriodHalf;} + +void FishController::start() +{ + + // Blink button board LEDs to indicate startup + for(uint8_t i = 0; i < 3; i++) + { + buttonBoard.setLEDs(255, true); + wait_ms(500); + buttonBoard.setLEDs(255, false); + wait_ms(500); + } + +#ifdef FISH6 + buoyancyControlUnit.start(); + pumpWithValve.start(); +#endif + + // Start control ticker callback + ticker.attach_us(&fishController, &FishController::tickerCallback, tickerInterval); +#ifdef debugFishState + printf("Starting...\n"); +#endif + + +} + +void FishController::stop() +{ + // Stop updating the fish + while(inTickerCallback); // wait for commands to settle + ticker.detach(); // stop updating commands + wait_ms(5); // wait a bit to make sure it stops + + // Reset fish state to neutral + newSelectButton = resetSelectButtonValue; + newPitch = resetPitchValue; + newYaw = resetYawValue; + newThrust = resetThrustValue; + newFrequency = resetFrequencyValue; + newPeriodHalf = resetPeriodHalfValue; + // Send commands to fish (multiple times to make sure we get in the right part of the cycle to actually update it) + for(int i = 0; i < 200; i++) + { + tickerCallback(); + wait_ms(10); + } + // Make sure commands are sent to motors and applied + wait(1); + + #ifdef FISH4 + // Put dive planes in a weird position to indicate stopped + servoLeft = 0.3; + servoRight = 0.3; + #endif + + #ifdef FISH6 + pumpWithValve.stop(); + buoyancyControlUnit.stop(); + #endif // FISH6 + + + // Light the LEDs to indicate termination + buttonBoard.setLEDs(255, true); +} + +//============================================ +// Processing +//============================================ +#ifdef FISH4 +void FishController::tickerCallback() +{ + inTickerCallback = true; // so we don't asynchronously stop the controller in a bad point of the cycle + + // get the current elapsed time since last reset (us) + curTime += tickerInterval; + + // see if brushless should be shut down + brushlessOff = curTime > (periodHalf-brushlessOffTime); + + // update every half cycle + if(curTime > periodHalf) + { + // read new yaw value every half cycle + yaw = newYaw; // a value from -1 to 1 + + // Read frequency only every full cycle + if(fullCycle) + { + // Read other new inputs + thrust = newThrust; // a value from 0 to 1 + frequency = newFrequency; + periodHalf = newPeriodHalf; + // Adjust thrust if needed + if(yaw < 0.0) + thrustCommand = (1.0 + 0.75*yaw)*thrust; // 0.7 can be adjusted to a power of 2 if needed + else + thrustCommand = thrust; + fullCycle = false; + } + else + { + // Reverse for the downward slope + if(yaw > 0.0) + thrustCommand = -(1.0 - 0.75*yaw)*thrust; + else + thrustCommand = -thrust; + fullCycle = true; + } + + // Reset time + curTime = 0; + } + + // Update the servos + + pitch = newPitch; + servoLeft = pitch - 0.05; // The 0.03 calibrates the angles of the servo + servoRight = (1.0 - pitch) < 0.03 ? 0.03 : (1.0 - pitch); + + // Testing whether fishController is running + // DigitalOut test(LED1); + // test = 1; + + // Update the duty cycle + /*dutyCycle = raiser * sin(PI2 * frequency * curTime); // add factor 4.0 to get a cut off sinus + if(dutyCycle > 1) + dutyCycle = 1; + if(dutyCycle < -1) + dutyCycle = -1; + dutyCycle *= thrustCommand; + if(dutyCycle >= 0 && dutyCycle < 0.01) + dutyCycle = 0; + if(dutyCycle < 0 && dutyCycle > -0.01) + dutyCycle = 0; + // Update the brushed motor + if(dutyCycle >= 0) + { + motorOutA.write(0); + motorOutB.write(1); + motorPWM.write(dutyCycle); + } + else + { + motorOutA.write(1); + motorOutB.write(0); + motorPWM.write(-1 * dutyCycle); + }*/ + // Update the brushless motor + //brushlessMotor = dutyCycle * !brushlessOff; + //brushlessMotor.pulsewidth_us(dutyCycle*500+1500); + //brushlessMotor(); + + +#ifdef debugFishState + printDebugState(); +#endif + //printf("%f\n", dutyCycle); + //printf("%f %f\r\n", pitch, servoLeft.read()); + inTickerCallback = false; +} +#endif + +#ifdef FISH6 +void FishController::tickerCallback() +{ + inTickerCallback = true; // so we don't asynchronously stop the controller in a bad point of the cycle + + //set current state to newly commanded value + frequency = newFrequency; + yaw = newYaw; + thrust = newThrust; + pitch = newPitch; + + // Update dive planes + servoLeft = pitch - 0.05; // The 0.03 calibrates the angles of the servo + servoRight = (1.0 - pitch) < 0.03 ? 0.03 : (1.0 - pitch); + + pumpWithValve.set(frequency, yaw, thrust); + + /* TURNING OFF BCU FOR FIRST OPEN WORLD TEST - AUGUST 21, 2019*/ + //buoyancyControlUnit.set(pitch); //1100 - 1180 seems to follow well + + //Testing whether fishController.tickerCallback() is running + //DigitalOut test(LED1); + //test = 1; + + +#ifdef debugFishState + printDebugState(); +#endif + + inTickerCallback = false; +} +#endif + + +// button will be mask indicating which button triggered this interrupt +// pressed will indicate whether that button was pressed or released +// buttonState will be a mask that indicates which buttons are currently pressed +void FishController::buttonCallback(char button, bool pressed, char state) // static +{ + //printf("button %d\t pressed: %d\t state: %d\n", button, pressed, state); + //fishController.buttonBoard.setLEDs(button, !fishController.buttonBoard.getLEDs(button)); + // Only act on button presses (not releases) + if(!pressed) + return; + + DigitalOut* simBatteryLow; + float newYaw, newThrust, newPitch; + switch(state) + { + case BTTN_YAW_LEFT: + newYaw = fishController.newYaw; + newYaw -= (fishMaxYaw - fishMinYaw)/4.0; + newYaw = newYaw < fishMinYaw ? fishMinYaw : newYaw; + fishController.setYaw(newYaw, true); + fishController.streamFishStateEventController = 6; + break; + case BTTN_YAW_RIGHT: + newYaw = fishController.newYaw; + newYaw += (fishMaxYaw - fishMinYaw)/4.0; + newYaw = newYaw > fishMaxYaw ? fishMaxYaw : newYaw; + fishController.setYaw(newYaw, true); + fishController.streamFishStateEventController = 7; + break; + case BTTN_FASTER: + newThrust = fishController.newThrust; + newThrust += (fishMaxThrust - fishMinThrust)/4.0; + newThrust = newThrust > fishMaxThrust ? fishMaxThrust : newThrust; + fishController.setThrust(newThrust, true); + fishController.streamFishStateEventController = 8; + break; + case BTTN_SLOWER: + newThrust = fishController.newThrust; + newThrust -= (fishMaxThrust - fishMinThrust)/4.0; + newThrust = newThrust < fishMinThrust ? fishMinThrust : newThrust; + fishController.setThrust(newThrust, true); + fishController.streamFishStateEventController = 9; + break; + case BTTN_PITCH_UP: + newPitch = fishController.newPitch; + newPitch += (fishMaxPitch - fishMinPitch)/4.0; + newPitch = newPitch > fishMaxPitch ? fishMaxPitch : newPitch; + fishController.setPitch(newPitch, true); + fishController.streamFishStateEventController = 10; + break; + case BTTN_PITCH_DOWN: + newPitch = fishController.newPitch; + newPitch -= (fishMaxPitch - fishMinPitch)/4.0; + newPitch = newPitch < fishMinPitch ? fishMinPitch : newPitch; + fishController.setPitch(newPitch, true); + fishController.streamFishStateEventController = 11; + break; + case BTTN_SHUTDOWN_PI: // signal a low battery signal to trigger the pi to shutdown + fishController.streamFishStateEventController = 12; + simBatteryLow = new DigitalOut(lowBatteryVoltagePin); + simBatteryLow->write(0); + break; + case BTTN_RESET_MBED: + fishController.streamFishStateEventController = 13; // ... if you see this, it didn't happen :) + mbed_reset(); + break; + case BTTN_AUTO_MODE: + fishController.streamFishStateEventController = 14; + if(fishController.autoMode) + fishController.stopAutoMode(); + else + fishController.startAutoMode(); + break; + case BTTN_BTTN_MODE: + fishController.setIgnoreExternalCommands(!fishController.getIgnoreExternalCommands()); + break; + default: + fishController.streamFishStateEventController = 15; + break; + } +} + +void FishController::setIgnoreExternalCommands(bool ignore) +{ + ignoreExternalCommands = ignore; +} + +bool FishController::getIgnoreExternalCommands() +{ + return ignoreExternalCommands; +} + +void FishController::startAutoMode() +{ + // Start ignoring external commands so as not to interfere with auto mode + // But remember what the previous setting was so we can restore it after auto mode + ignoreExternalCommandsPreAutoMode = ignoreExternalCommands; + setIgnoreExternalCommands(true); + // Reset state + autoModeCount = 0; + autoModeIndex = 0; + // Start executing the auto loop + autoMode = true; + autoModeTicker.attach_us(&fishController, &FishController::autoModeCallback, 10000); +} + +void FishController::stopAutoMode() +{ + autoModeTicker.detach(); + // Auto mode was terminated - put fish into a neutral position + setSelectButton(resetSelectButtonValue, true); + setPitch(resetPitchValue, true); + setYaw(resetYawValue, true); + setThrust(resetThrustValue, true); + setFrequency(resetFrequencyValue, resetPeriodHalfValue, true); + // Restore external mode to what is was previously + setIgnoreExternalCommands(ignoreExternalCommandsPreAutoMode); + autoMode = false; +} + +void FishController::autoModeCallback() +{ + // Assign the current state (stored as pitch, yaw, thrust, frequency) + setPitch(autoModeCommands[autoModeIndex][0], true); + setYaw(autoModeCommands[autoModeIndex][1], true); + setThrust(autoModeCommands[autoModeIndex][2], true); + setFrequency(autoModeCommands[autoModeIndex][3], 1.0/(2.0*autoModeCommands[autoModeIndex][3]), true); + // See if we advance to the next command + autoModeCount++; + if(autoModeCount*10 > autoModeDurations[autoModeIndex]) + { + autoModeCount = 0; + autoModeIndex = (autoModeIndex+1) % autoModeLength; // loop continuously through commands + } +} + +#ifdef debugFishState +void FishController::printDebugState() +{ + printf("pitch: %f yaw: %f thrust: %f frequency: %.8f\r\n", + pitch, yaw, thrust, frequency); +} + +#endif + +void FishController::setLEDs(char mask, bool turnOn) +{ + buttonBoard.setLEDs(mask, turnOn); +} + +#ifdef debugBCU +/* BCU + Pressure Sensor Helper Functions */ + +float FishController::getBCUVset(){ return buoyancyControlUnit.getVset(); } +float FishController::getBCUSetDepth(){ return buoyancyControlUnit.getSetDepth(); } +float FishController::getBCUCurDepth(){ return buoyancyControlUnit.getCurDepth(); } +float FishController::getBCUSetPos(){ return buoyancyControlUnit.getSetPos(); } +float FishController::getBCUCurPos(){ return buoyancyControlUnit.getCurPos(); } +float FishController::getreadPressure(){ return buoyancyControlUnit.readPressure(); } + +#endif \ No newline at end of file