Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Step counter history added #2120

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

Hunman
Copy link

@Hunman Hunman commented Sep 8, 2024

I changed the MotionController to remember the past N (currently 2) day's step counter history after the midnight reset. And I also changed the Steps screen to display (and update) yesterday's step count in addition to today's.

I didn't feel comfortable changing the return type of MotionService::NbSteps() yet without feedback, instead I wrote a new method to return the whole history.

I've tested this only using InfiniSim, testing with devkits would be welcome!

In the future I would like to increase the history size from 2 to 7 and transfer the whole history to the companion applications, but I haven't really looked into how bluetooth communication or "API"s work, so feedback would be most appreciated!

Copy link

github-actions bot commented Sep 8, 2024

Build checks have not completed. Possible reasons for this are:

  1. The checks need to be approved by a maintainer
  2. The branch has conflicts
  3. The firmware build has failed

@minacode
Copy link
Contributor

minacode commented Sep 9, 2024

Nice! You should be able to test this on your sealed watch without problems. As long as you don't verify the firmware, a reboot will be enough to get it back to working. And if you do verify it, a force reboot into the backup firmware still enables you to install another firmware.

Copy link
Contributor

@mark9064 mark9064 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool feature, looking good :) haven't tried it yet on my PT yet but hopefully will soon

if (this->nbSteps != nbSteps && service != nullptr) {
void MotionController::AdvanceDay() {
--nbSteps; // Higher index = further in the past
nbSteps[today] = -1; // Ensure that the `this->nbSteps[today] != nbSteps` condition in `Update()` will be FALSE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be better to assign zero here and do service->OnNewStepCount() inside this function. Reasoning about -1 applied to an unsigned int is complex and so is reasoning about the delta steps calculation

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -18,7 +19,17 @@ namespace Pinetime {
BMA425,
};

void Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps);
enum Days {
today = 0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enum members should be capitalised

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

yesterday = 1,
};

using step_t = uint32_t;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm really split on this one. On one hand it's nice to have this as a type. On the other, I can't see this type ever being changed (nobody is ever gonna exceed 4 billion steps in a day :P) and it is slower to read and more verbose than uint32_t. Also IIRC C++ still will allow using step_t and uint32_t interchangeably right? (and perhaps any larger/smaller integer type depending on the context). If that's true then we don't get any safety benefits either

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was torn when I wrote this too :D

Yeah, it is unlikely to change, since the hardware returns a uint32_t through bma423_step_counter_output() and since it's an alias, step_t is completely interchangeable with uint32_t, so having this does not make the code any safer or faster.

But on the other hand, it is more verbose, potentially making the code easier to read (at least for me) that this function returns or expects a type in which steps are stored (even though the function/parameter name should be enough), much like how size_t is used for most if not all functions where a container size is used.

At least that was my rationale when I wrote this, but I can change it back to uint32_t if needed (or move it out of the MotionController class).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or I could make it a real type instead of an alias, so they wouldn't be interchangeable.

Copy link
Contributor

@mark9064 mark9064 Sep 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think when it comes to verboseness what I mean is having to reference Controllers::MotionController::step_t instead of uint32_t. To me uint32_t is easier to read, with the operations available on the type being clearer rather than having to look it up and go "oh, it's just an unsigned int, right". But your point about the semantics of the variable being clearer is fair.

I don't know, maybe someone else will have an opinion? I don't strongly oppose it either way

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just a count, right? Maybe keep it simple. I don't see, what problem would be solved, or is there any critical code, where confusion must not happen (e.g. temperature, where we handle values with different units)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's just a counter, I just didn't feel important to know the real underlying type everywhere in the code.

Even if step_t makes sense, it definitely shouldn't be defined within MotionController, so I'm removing it

@@ -32,15 +43,19 @@ namespace Pinetime {
return zHistory[0];
}

uint32_t NbSteps() const {
step_t NbSteps() const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be better if NbSteps took a member of Days as an argument (as you suggest actually)
The step counter history method is still needed though if you plan on exposing this over BLE

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

I've set Today as default parameter.

Should I revert most of the changes to Steps.cpp, where I'm storing a const reference to nbSteps?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would personally as I think calling NbSteps() is a bit clearer than using the buffer reference

@@ -440,6 +440,7 @@ void SystemTask::UpdateMotion() {

if (stepCounterMustBeReset) {
motionSensor.ResetStepCounter();
motionController.AdvanceDay();
Copy link
Contributor

@mark9064 mark9064 Sep 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I suggest removing stepCounterMustBeReset and the associated message as they aren't needed anymore? (as I2C is never slept) This would cause the step count handling to be done immediately when a new day starts. It's a little out of scope for this PR technically but it needs to be refactored anyway and I reckon now is a sensible time to do it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It indeed sounds a bit out of scope

I can open a pull request only with this after this one if that's alright

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries, I'll get round to it

lv_obj_t* stepsArc;
lv_obj_t* resetBtn;
lv_obj_t* resetButtonLabel;
lv_obj_t* tripLabel;

uint32_t stepsCount;
const char* yesterdayStr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could yesterdayStr be a constant?
If that's fine, you could stick it in an anonymous namespace in the implementation as constexpr const char * (I think!) and then it will live in just flash rather than flash and RAM

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory that's how it should work right now too, because I'm not copying the string, just taking the address of a string literal (that's why I don't need to free it either)

Unless I'm misunderstanding something

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't matter here too much as it's heap memory rather than stack, but you can avoid storing the pointer at all by effectively inlining the constant

Last,
};

static constexpr size_t stepHistorySize = static_cast<std::underlying_type_t<Days>>(Days::Last); // Store this many day's step counter
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NbSteps(Days::Last) is out of bounds with this implementation right? I think that could be better, I'd expect every entry of the enum to be valid. Perhaps do LastEntry = Yesterday and then static_cast<std::underlying_type_t<Days>>(Days::LastEntry) + 1? Also probably assign an integer value explicitly to every enum member as well for clarity (except LastEntry as above)

My other concern is that there's no way to get all of the days without having an enum entry for each one. If we want to expose 7 days over BLE, at the moment all 7 days would need to be named in the enum which feels a bit odd. Not a dealbreaker though

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, theoretically out of bounds. In practice the CircularBuffer handles it internally by doing modulo (%) operation on the size, so Days::Last would be just index 0 again.

While it is nice to refer to the days with Today and Yesterday, maybe having to name 5 more enum values is not that nice.

One option is that the Days enum can be removed altogether. Or alternatively it can remain with only Today and Yesterday values, and NbSteps() function could get an overload that expects a uint8_t.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That second option sounds good to me, that way we have a nice human readable enum while also allowing specifying a number of days ago for more flexible usage

@mark9064
Copy link
Contributor

Sorry for the seemingly endless review! It's looking really good now, almost there

@Hunman
Copy link
Author

Hunman commented Sep 12, 2024

It's alright, I'm actually enjoying this opportunity to "nerd out" with people on a project 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants