All mbed code for control over dive planes, pump motor, valve motor, BCUs, UART interface, etc.
Dependencies: mbed ESC mbed MODDMA
robotic_fish_6/FishController.cpp
- Committer:
- juansal12
- Date:
- 2020-01-14
- Revision:
- 0:c3a329a5b05d
File content as of revision 0:c3a329a5b05d:
#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