src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
4 / 4
100.0%
Branches:
5 / 10
50.0%
Functions:
4 / 4
100.0%

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
5/10
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 10 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 10 times.
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