"Twist" Music Control

Posted on
  • Hey there,

    first of all, thanks to everyone who created Bangle.js. Without this piece of hard- and software I would've never started to try writing code.
    In the last two days I tried to write an app (never wrote any code before, I hope it's not too "ugly") to control a music player on Android, which can be controlled by multiple play-commands (1x play / 2x next song / 3x previous song / 4x next folder / 5x previous folder), by a twist of your arm. The idea behind this was: there may be situations when you second hand isn't free, but you wanna change the song you're listening to.

    I had some problems, which I solved very intricately (is this the right word? I'm no native english speaker :sweat_smile: ) I guess.

    Maybe somebody wants to take a look and tell me how to handle things better or less intricate?

    /////////////////////////////////////////////////////////////
    //   control music by twist/buttons
    
    let counter = 0;
    var tstate = false;
    
    // defining commands
    function playx() {
      Bluetooth.println(JSON.stringify({t:"music", n:"play"}));
    }
    
    function volu() {
      Bluetooth.println(JSON.stringify({t:"music", n:"volumeup"}));
    }
    
    function vold() {
      Bluetooth.println(JSON.stringify({t:"music", n:"volumedown"}));
    }
    
    function resetc() {counter=0;}
    
    function cstate() {
      tstate=!tstate;
      setTimeout(resetc,100);
      Bangle.beep(200,3000);}
    
    function countup() {
      if (counter < 5){
        counter++;
        Bangle.buzz(100,2);
        }
      else {counter = 0;
            Bangle.buzz(400);
           }
    }
    
    function sendCmd() {
      print (counter);
      Bangle.removeListener('twist',countup);
      if (tstate==false && counter>0){
      do {playx(); counter--;}
      while (counter >= 1);
      }
    }
    
    function twistcount() {
      if (tstate==true){
      Bangle.on('twist',countup);
      }
    }
    
    function twistctrl() {
      if (tstate==false)
      {cstate();
      twistcount();
      setTimeout(cstate,4000);
      setTimeout(sendCmd,4100);
      }
    }
    
    setWatch(volu,BTN1,{repeat:true});
    setWatch(vold,BTN3,{repeat:true});
    Bangle.on('twist',twistctrl);
    setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
    

    Is this the right place for this thread, or is "Projects" the better place?

  • Hi,

    You say it's basically the first bit of code you've written? I wish the first code I wrote looked so good.

    So it's actually all working absolutely fine for you? You're just asking if we have any advice about how you've done it?

  • Well, I went through the Bangle.JS First App and Clock Faces tutorials and spent some time reading code on github. But this was the first time I've done something without a tutorial and tried to get my ideas into codelines (the API Reference helped a ton and mozillas MDN JavaScript reference was very helpful too). It took me quite a while in the last two days to figure things out, but it's so satisfying if problems get solved :D

    It is working for me but... there are some things happening, I don't really understand.
    After loading the code into RAM, I twist to start counting -> Bangle beeps and starts counting my following twists confirming them with short vibrations and sends a BL command to my phone and beeps after 4s - that's what it should do. But on the second twist to start counting, it also vibrates and counts up one. Thats why I had to add a "reset counter" (line 20) function to be executed in line 24. If I hadn't done that, every counting would start with the counter set to 1 instead of 0.
    But what causes the +1 before the "real" countup starts?

    At first attempt I ran the code without Bangle.removeListener(). That caused added countup-functions everytime I started a new "start counting my twists"-twist. At this point I almost gave up until I discovered that you can "stop" Bangle.on('twist')-functions with those "removeListeners". Then I added Bangle.removeAllListeners(). which stopped those multiple countups at once, but also stopped the inital twist-listener for the "start counting my twists"-function after sending the commands first time. Then I tried "Bangle.removeListener('twist',countup)", which seems to have solved it.
    So it was a lot of try and error and looking up things. There's still a lot of syntax I don't understand.

    So the reasons why I'm asking are on the one hand the "problems" I mentioned above and on the other hand I want to improve it so one day it's fine enough to find it's way into the App Loader so others can use it too, if they want. :)

    I hope my text is kind of understandable

  • Tue 2021.05.18

    'I had some problems, which I solved very intricately (is this the right word?'

    intricately - 'in a very complicated or detailed manner'

    https://www.google.com/search?q=intricately+definition&oq=intricately

    While I'll agree the English language, at times can be quite complicated, I'm trying to find a more correct word, but finding it difficult to truly understand what means were used for the 'which I solved' statement based on the perceived 'problems'.

    these also come to mind

    complexity - 'a factor involved in a complicated process or situation'

    https://www.google.com/search?q=complexity+definition&oq=complexity

    intuitive - 'what one feels to be true even without conscious reasoning'

    https://www.google.com/search?q=intuitive+definition&oq=intuitive

    simply - 'in a straightforward or plain manner'

    https://www.google.com/search?q=simply+definition&oq=simply+definition

     
    'Intuitive' seems to be the best fit.

    Enough of the nit-picky off topic stuff and on to what we all love to do!!


    'Is this the right place for this thread, or is "Projects" the better place?'

    Yes, as this is more of a coding issue, the 'Projects' area IMO was created for end users to demonstrate their completed works.



    'never wrote any code before, I hope it's not too "ugly"'

    I too agree with Gordon here, @radswid as you are demonstrating key techniques early on, such as: naming order; variable definitons before function definitions, short declaritive naming convention, (which BTW I also prefer the obj-action or obj-state manner such as 'volup' 'voldn') command of the language statements, with both syntax and usage, and are now at a wall with command of the 'run-time' error's or in this case more of a run-time anomaly where actual functionality just isn't quite what is envisioned. And, those are the toughest to resolve, I'm afraid.

    You strike me as more of a solution oriented problem solver where you would garner more satisfaction learning to solve the issue yourself, and that is how I prefer to present possibilities, rather than provide the solution.

    The 80-20 Pareto axiom

    https://www.investopedia.com/terms/1/80-20-rule.asp

    For software development, I find it is closer to 60-40 or 70-30 for me, where 40% of my efforts (time) get's me to 60% of a solution (completion). You are on the right track to your solution, so let's put a bit of polish on it while learning through experience, shall we?

    While I haven't installed your snippet, no Bangle to test on, I believe I have detected an area that needs a bit of attention. The twist event L63 fires function twistctrl(). L52 responds to that event, firstly checking the state of the twist state variable 'tstate'. But what happens should another twist event occur, and the state of 'tstate' is actually 'true'? Ans: as you will astutely observe - nothing

    So this is where I believe the thinking path was to then toy with listeners.

    EDIT: counter=0 found L32 and later function L20 after post #3
    May I ask what the thinking was to embed the reset counter call within a timeout in L24? There likely was a valid reason, however as an observer, I'm not sure the delay is actually needed.

    I also don't see where variable 'counter' is ever reset. This likely is also contributory.
    Hint: test for both conditions in this one function for starters

    Moving forward, a bit of re-thinking will be needed. Would you prefer to further your knowledge by tackling on your own as my assessment suggests, or are you at a point where total frustration has now set in and moving forward is just too painful?

    I know others are dying to jump in with their solution, however, there will be more joy and satisfaction solving this on your own as you have made tremendous strides in a near complete solution to start with.

  • Hey Robin, thanks a lot for your detailed answer!
    The counter call within a timeout in L24 is caused by the different timeouts on lines 56 & 57. If it would be reset before sendCmd is executed, sendCmd would always have counter=0 and therefore never send any commands to my phone, was my line of thought.

    The only thing I consider to be the possible problem is in L39 "Bangle.removeListener('twist',countup);". Maybe I don't get this function right. I wanted to remove the listener that looks for twists and executes "countup" then. Is line39 removing the listener and also executing countup once?
    EDIT: just tested to change the L39 into "Bangle.removeListener('twist');", which causes errors and breaks the functionality. So I guess, it was right in first place?

    (regarding the "nit-picky" off topic stuff: "tackling sth in a roundabout way" may be what I wanted to say. I guess one could write this "functionality" with less functions, so it preserves RAM and battery. So I was also looking for hints to do so ;-) )

  • Hi!

    I'd say you'd probably find it easier having just Bangle.on('twist',twistctrl); and then putting all your code in twistctrl (rather than adding/removing countup as needed). I think that could really tidy things up for you, and might fix some of the quirkiness.

  • Thanks a lot for pointing me in the right direction. I lacked the knowledge to realize that I don't need a second Bangle.on('twist'). And now I realize my newbie question is quite senseless, because a bit more of "if...else-understanding" gets the job done easily.

    So here is the less function-cluttered "app":

    /////////////////////////////////////////////////////////////
    //   control music by twist/buttons
    
    let counter = 0; //stores your counted your twists
    var tstate = false; //are you ready to count the twists?
    
    function playx() {
      Bluetooth.println(JSON.stringify({t:"music", n:"play"}));
    }
    
    function volu() {
      Bluetooth.println(JSON.stringify({t:"music", n:"volumeup"}));
    }
    
    function vold() {
      Bluetooth.println(JSON.stringify({t:"music", n:"volumedown"}));
    }
    
    function sendCmd() {
      print (counter);
      Bangle.beep(200,3000);
      if (tstate==false && counter>0){
      do {playx(); counter--;}
      while (counter >= 1);
      }
    }
    
    function twistctrl() {
      if (tstate==false){
        tstate=true;
        setTimeout('tstate=false',4000);
        setTimeout(sendCmd,4100);
        Bangle.beep(200,3000);
      }
      else{
      if (tstate==true){
      if (counter < 5){
        counter++;
        Bangle.buzz(100,2);
        }
      else {
        counter = 0;
        Bangle.buzz(400);
           }
      }
      }
    }
    
    setWatch(volu,BTN1,{repeat:true});
    setWatch(vold,BTN3,{repeat:true});
    Bangle.on('twist',twistctrl);
    setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
    
  • Wed 2021.05.19

    'I realize my newbie question is quite senseless'

    On the contrary, . . . it was necessary to learn the if-else construct prior to being able to realize that the second twist event would not be required. In your mind before that eureka moment, there wasn't a means to get to that realization, so at that point, creating this post was one of the only means to get to the solution you found. Quite logical when one views this thread thinking the events in reverse.

    Using Gordon's #6 post which is a refinement of the hint I provided in #4 post, allowed you to resolve in a very short period of time the solution I (we) intended you to get to. So, @radswid in around a 24hr period, you were able to comprehend what we were communicating, even though was in more of a hint format, rationalize how that needed to fit into what sequential event sequence wasn't occurring, use intuition and maybe a bit of language syntax learning lookup, in order to present a better structured and simpler coding result, code and debug that solution and all in one evening to-boot!! Are you sure you are a beginner??    ;-)

    Now, please help me here. I'd like to create some tutorials (not related to this project) and have my own references. You indiated in post #3 two references, (the API Reference helped a ton and mozillas MDN JavaScript reference was very helpful too) are there by chance any others that you are using that helped here?

    Now that the final solution is near, there are a couple of observations I'd like to make. Please don't take this as criticism, as it usually takes quite a bit of time before patterns and coding techniques are mastered. And since this solution was found overnight, that hardly fits into
    the 'quite a bit of time' duration.

    Carefully review L36 in post #7 and review examples at MDN:

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else

    The hint here is to simplify. See how that task now may be an improvement.

    I have several others, that we may tackle one-at-a-time.

  • Thanks for your kind words, @Robin.

    'I realize my newbie question is quite senseless'

    I wanted to express, that I feel a bit guilty asking questions which could've been answered by myself by a revision of my code and re-reading some tutorial-parts. But maybe you're right, sometimes you need a kind of other "input" to get to a solution.

    Are you sure you are a beginner??

    Before starting this project I copypasted tutorial-codesnippets into the IDE or tried changing code-parts of e.g. a clock app to change its look. So I guess so.

    You indiated in post #3 two references, (the API Reference helped a ton and mozillas MDN JavaScript reference was very helpful too) are there by chance any others that you are using that helped here?

    I tried several Youtube-tutorials, but never found one which focuses on javascript without combining it with html and css. So the great tutorials on espruino.com and those mentioned references were the only "sources of knowledge" for me.

    Carefully review L36 in post #7 and review examples at MDN:

    Ah, it's obviously unnecessary, as there are only two conditions for tstate :D . Should've rechecked before posting.

    Thanks again for spending your time on my gain of knowledge! Really appreciate that!

  • Thr 2021.05.20

    'So the great tutorials on espruino.com'

    I believe that Gordon is the main creator of that content and deserves the credit.

    'I tried several Youtube-tutorials'

    Myself, I prefer the text snippet layout that currently exists and it's actually what drew my attention to Espruino in the first place.

    Do you feel then, that having more video tutorials would be a better alternative?


    'I wanted to express, that I feel a bit guilty asking questions which could've been answered by myself by a revision of my code'

    Hey @radswid go easy on yourself!! I'm impressed recognizing the 'intuition' you are using to solve these miniscule anomalies yourself, and completed in two days!! No one gave you the answers. All I did was point the compass in the correct direction to allow you to turn over those stones that were hiding the treasure. (or as allObjects might relate to, trim the sail to better tack into the wind ;-)



    Regarding: 'Are you sure you are a beginner?? . . . . So I guess so.'

    As I recognized your skills in a near complete solution with minimal upfront knowledge, My reason for asking was that should you be younger and seeking a career, that as you possess the fundamental skills that those that continue on also expand on, seriously consider software development. You were able to read into suggestions, the 'Think-Do-Think' mindset, then translate that to your project using 'Do-Think-Do'. . . . and, . . . Viola`! your working code block. Impressive skill set indeed!!



    Really nit-picky here, and I'm not the one to qualify even this assessment, 'let vs. var'

    My understanding is that 'let' was intended for block scope within functions.

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
    https://stackoverflow.com/questions/37916940/why-was-the-name-let-chosen-for-block-scoped-variable-declarations-in-javascri

    While either will work in your code block as global scope rules apply, there may be a reason for one over the other. Maybe the need for another thread here, or analysis by @allObjects, @AkosLukacs, etal



    Request for @Gordon completion here:

    As I have not studied up on Bangle Apps and deployment, I'm not sure if the following should be done, or even if it is possible.

    Q1: Should the App itself contain startup logic as described in the following, is it wise to do so?

    launching page - sample snippets

    Proper way to call init() on start up - E.on() - onInit()

    Great read written by allObjects

    simple explanation how to save code that espruino run on start?



    Q2: Is is possible to turn the App itself into a module, pros and cons doing so?

  • Hi @Robin,

    Maybe you could check out the Bangle.js tutorials first, specifically https://www.espruino.com/Bangle.js+First+App

    The apps are each individual JS files that are run in one go when the app is loaded. It's as if you do Upload to Storage with the Web IDE: http://www.espruino.com/Saving#to-storage

    It's not like using save(), so the whole idea of onInit/etc doesn't make too much sense since your code is all executed at once anyway.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

"Twist" Music Control

Posted by Avatar for radswid @radswid

Actions