ShadeMotion.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #ifndef SHADE_MOTION_H | ||
| 2 | #define SHADE_MOTION_H | ||
| 3 | #include <Arduino.h> | ||
| 4 | #include "TxBuffer.h" | ||
| 5 | #include "ShadeTypes.h" | ||
| 6 | |||
| 7 | // Shade position maths. Kept free of the shade state, radio and network so it | ||
| 8 | // can be unit tested on the PC (pio test -e native). | ||
| 9 | |||
| 10 | // Where a shade (or its tilt) is after travelling elapsedMs from startPos | ||
| 11 | // (0-100). dir > 0 moves toward 100 and dir < 0 toward 0. travelMs is the | ||
| 12 | // time a full travel takes in that direction, 0 meaning it gets there at once. | ||
| 13 | float travelPosition(float startPos, int8_t dir, uint32_t elapsedMs, int32_t travelMs); | ||
| 14 | // A position as reported to clients: -1 when unset, otherwise the whole | ||
| 15 | // percentage, counted from the other end when flipped. | ||
| 16 | int8_t displayPosition(float pos, bool flip); | ||
| 17 | // The tilt a request to set the favorite asks for, as a display position: the | ||
| 18 | // requested tilt, or the favorite tilt when the request leaves it out. -1 for | ||
| 19 | // a shade without tilt or with no favorite tilt. | ||
| 20 | int8_t requestedFavoriteTilt(int8_t requestTilt, tilt_types tilt, float myTiltPos, bool flip); | ||
| 21 | // Where a shade (or its tilt) moving in dir ends up when nothing stops it. | ||
| 22 | float limitTarget(int8_t dir); | ||
| 23 | // Where a step command moves a shade (or its tilt) from pos, in out. dir > 0 | ||
| 24 | // steps toward 100 and dir < 0 toward 0. A step is stepSize * frameStepSize | ||
| 25 | // ms of the travelMs a full travel takes in that direction. False when | ||
| 26 | // travelMs or stepSize is 0, so the step is ignored. | ||
| 27 | bool stepPosition(float pos, int8_t dir, uint32_t travelMs, uint16_t stepSize, uint8_t frameStepSize, float &out); | ||
| 28 | // What a step command does to a shade. | ||
| 29 | enum class StepAction : uint8_t { | ||
| 30 | ignore, // The step can't be sized or the shade is at both ends: drop it. | ||
| 31 | none, // A lift shade already at the end: nothing moves. | ||
| 32 | move // Set the targets below. | ||
| 33 | }; | ||
| 34 | struct StepTargets { | ||
| 35 | StepAction action = StepAction::ignore; | ||
| 36 | bool setLift = false; | ||
| 37 | float lift = 0.0f; | ||
| 38 | bool setTilt = false; | ||
| 39 | float tilt = 0.0f; | ||
| 40 | }; | ||
| 41 | // Where a step in dir (> 0 down, < 0 up) takes a shade at pos and tiltPos. A | ||
| 42 | // tilt-only shade steps its tilt. An integrated tilt turns its slats to the | ||
| 43 | // end first, holding the lift, and then steps the lift. Every other shade | ||
| 44 | // steps its lift. | ||
| 45 | StepTargets stepTargets(tilt_types tilt, int8_t dir, float pos, float tiltPos, | ||
| 46 | uint32_t upTime, uint32_t downTime, uint32_t tiltTime, uint16_t stepSize, uint8_t frameStepSize); | ||
| 47 | |||
| 48 | // What a held shade does on this pass of checkMovement(). | ||
| 49 | enum class TxHoldCheck { | ||
| 50 | none, // Nothing is held. | ||
| 51 | holding, // Still waiting for the job to go on air. | ||
| 52 | expired // Waited too long, so the hold was dropped and the shade moves anyway. | ||
| 53 | }; | ||
| 54 | // The radio job that set an idle shade moving. Its simulated movement is held | ||
| 55 | // until the job goes on air, since that is when the motor starts. | ||
| 56 | struct TxHold { | ||
| 57 | uint32_t ticket = 0; // 0 when nothing is held. | ||
| 58 | uint32_t since = 0; // millis() when the hold started. | ||
| 59 | // Holds for job ticket unless the shade was already moving (its motor carries on | ||
| 60 | // with the earlier command) or a job is already held. The first job held is | ||
| 61 | // the one that starts the motor. A command sent while processing another | ||
| 62 | // registers before it: for a simMy shade My/Favorite queues its Up/Down from | ||
| 63 | // moveToMyPosition() first, and that Up/Down is what starts a motor with no | ||
| 64 | // favorite of its own, so the outer My/Favorite is ignored on purpose. | ||
| 65 | void mark(uint32_t ticket, bool moving, uint32_t now); | ||
| 66 | // Ends the hold when ticket is the held job. True when it was. | ||
| 67 | bool release(uint32_t ticket); | ||
| 68 | 16370 | bool held() const { return this->ticket != 0; } | |
| 69 | 3210 | bool timedOut(uint32_t now, uint32_t timeoutMs) const { return now - this->since >= timeoutMs; } | |
| 70 | 5 | void clear() { this->ticket = 0; } | |
| 71 | // Drops a hold on a shade the command didn't set moving, or one that has | ||
| 72 | // waited timeoutMs for the radio. | ||
| 73 | TxHoldCheck check(bool moving, uint32_t now, uint32_t timeoutMs); | ||
| 74 | }; | ||
| 75 | // What a shade has to do about a radio event. More than one can apply to the | ||
| 76 | // same event. | ||
| 77 | struct ShadeTxActions { | ||
| 78 | // The held job went on air, so the motor starts at startAt. | ||
| 79 | bool startMovement = false; | ||
| 80 | uint32_t startAt = 0; | ||
| 81 | // The held job never went on air, so the motor hasn't moved. | ||
| 82 | bool cancelMovement = false; | ||
| 83 | // The favorite went on air in full and is committed, or it didn't and the | ||
| 84 | // previous one is put back. | ||
| 85 | bool commitMy = false; | ||
| 86 | bool restoreMy = false; | ||
| 87 | float myOldPos = -1.0f; | ||
| 88 | float myOldTiltPos = -1.0f; | ||
| 89 | // The stop never went on air, so the motor runs on to its end limit from | ||
| 90 | // where and when it should have stopped. | ||
| 91 | bool runToLimit = false; | ||
| 92 | int8_t limitDir = 0; | ||
| 93 | bool limitTilt = false; | ||
| 94 | uint32_t limitSince = 0; | ||
| 95 | 10 | bool any() const { return startMovement || cancelMovement || commitMy || restoreMy || runToLimit; } | |
| 96 | }; | ||
| 97 | // The radio jobs a shade waits on to find out what its motor really did. | ||
| 98 | struct ShadeTxState { | ||
| 99 | // Radio job that set the shade moving. The simulated position is held | ||
| 100 | // until it goes on air. | ||
| 101 | TxHold hold; | ||
| 102 | // Stop sent when the simulated target was reached. If it never goes on | ||
| 103 | // air the motor runs on to its end limit. 0 once an event for it arrived. | ||
| 104 | uint32_t stopTicket = 0; | ||
| 105 | uint32_t stopAt = 0; | ||
| 106 | int8_t stopDir = 0; | ||
| 107 | bool stopTilt = false; | ||
| 108 | // Command telling the motor to store the favorite. The favorite is only | ||
| 109 | // committed once all of it is on air and restored if it never is. | ||
| 110 | uint32_t myTicket = 0; | ||
| 111 | float myOldPos = -1.0f; | ||
| 112 | float myOldTiltPos = -1.0f; | ||
| 113 | void stopSent(uint32_t ticket, uint32_t now, int8_t dir, bool tilt); | ||
| 114 | void setMySent(uint32_t ticket, float oldPos, float oldTiltPos); | ||
| 115 | // What to do about evt. moving is whether the shade is moving now. | ||
| 116 | ShadeTxActions onEvent(const somfy_tx_event_t &evt, bool moving); | ||
| 117 | }; | ||
| 118 | #endif | ||
| 119 |