Oh yes… programming

This post is part 4 of 5 of  LEGO bagpiper

Programming the LEGO Technic Hubs with Pybricks is not difficult (much, much more easier than using assembly with PIC controllers in the 90’s). But one thing is making motors move the way you want and other is making a choreography that sounds like music.

The chanter has 8 holes – 7 are placed above, the other one is downside. There are several sites on the web showing the proper fingertips positions and more than a few adopted a notation of black and white circles (representing closed and open holes). Much like binary notation.

So I chose to use ‘0’ for motor in rest position (closing the hole) and ‘1’ for motor in some other position that opens the hole. So a single byte can represent any possible note (and of course also lots of other combinations).

Using the most significant bit for the higher pitch hole (the one downside) and the less significant bit for the low pitch hole (the one near the end tip of the chanter) this is the result:

   G = 0 0 0 0 0 0 0 0
   A = 0 0 0 0 0 0 0 1 
   B = 0 0 0 0 0 0 1 1
   C = 0 0 0 0 0 1 1 0
   D = 0 0 0 0 1 1 1 0
   E = 0 0 0 1 0 0 0 1
   F = 0 0 1 1 0 0 0 1
HI_G = 0 1 1 1 0 0 0 1
HI_A = 1 1 1 0 0 0 0 1

As I am (was) using two hubs, it seemed logic to group each representation in two nibbles of 4 bits so the same program running on both Hubs could position its 4 fingers just by receiving a char with a value between ‘0’ (all holes closed) and ‘F’ (all holes open):

   G = 0 0 0 0 | 0 0 0 0 = 0 | 0
   A = 0 0 0 0 | 0 0 0 1 = 0 | 1
   B = 0 0 0 0 | 0 0 1 1 = 0 | 3
   C = 0 0 0 0 | 0 1 1 0 = 0 | 6
   D = 0 0 0 0 | 1 1 1 0 = 0 | E
   E = 0 0 0 1 | 0 0 0 1 = 1 | 1
   F = 0 0 1 1 | 0 0 0 1 = 3 | 1
HI_G = 0 1 1 1 | 0 0 0 1 = 7 | 1
HI_A = 1 1 1 0 | 0 0 0 1 = E | 1

The main program loop would be in fact just a bunch of if-then-elif’s based on the key pressed (if using Pybricks Chrome-based IDE) or the character received (if using pybricksdev or accessing the Nordic UART Service directly):

while True:
    if keyboard.poll(0):
        char = stdin.read(1)
        if char == '0': #0000
                fingerA_down()
                fingerB_down()
                fingerC_down()
                fingerD_down()
        elif char == '1': #0001
                fingerA_down()
                fingerB_down()
                fingerC_down()
                fingerD_up()
        elif...

and this really worked – pressing some keys on Chrome made the fingers move.

But that’s not enough for playing music. What movements should be done and for how long for this apparently random fingering to produce something that doesn’t sound like me stepping on a cat’s tail?

Ardu McDuino for the rescue.

XenonJohn was gentle enough to include his Arduino code with the Instructables for his Bagpipe Playing Robot. He used a 3-char notation for his music where the first char represents an embellishment (like a gracenote or a doubling), the second char represents the main note and the last char represents the duration of the note. For instance:

-GR

means playin a ‘G’ for 1/4 of the time of a full duration note (I believe this means a ‘quarter note’ or a ‘crotchet’) with no embellishment.

And he also included two songs in his source code: ‘Amazing Grace’ and ‘Scotland The Brave’.

Now this is something I can use.

Instead of implementing his whole code with Pybricks I just adapted his song sequence. I opted for not representing embellishments since they can be represented by (fast) sequences of finger positions (assuming that there is enough bandwidth between the “controller” and each hub to send everything and the hub processes everything fast enough… if I find that is not the case I might return to XenonJohn’s notation).

So this is my way of representing a song:

sequence = [
A, R,
D, Z,
F, R,
E, N,
D, N,
F, Z,
]

where the first character is the note and the second character is the duration (still using Ardu McDuino, too lazy to change that for now):

j = 1 = whole note (semibreve)
Z = 1/2 = half note (minim)
R = 1/4 = quarter note (crotchet)
N = 1/8 = eight note (quaver)
L = 1/16 = sixteenth note (semiquaver)
K = 1/32 = thirty-second note (demisemiquaver)

But for gracenotes this doesn’t work good since a grace note is played for 1/32 and the next note should be reduced from this value. So I had three timing notations for the 3 cases where a gracenote is used in Ardu McDuino songs (before a note with a timing of R, Z or N) reducing these timing by 1/32:

x = R – 1/32 = 1/4 – 1/32 = 1/4.57
y = N – 1/32 = 1/8 – 1/32 = 1/10.67
z = Z – 1/32 = 1/2 – 1/32 = 1/2.13

these look strange but are easy to use in my code since I define a tempo for my song and each timing is just the multiplication of this tempo by this numbers.

First attempt looked promising but not quite right, then I find a silly math error and now I finally have something that sounds familiar:

Series Navigation<< A whole week improving fingertipsHot fingers >>

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *