Led Race-v0.0.0

linea orizzontale

FabLab Valsamoggia

Referenti progetto:

Christian Corsetti

Stefano Caiazzo

Stefano Fragomeni

COS’E’

Una semplice pista per le gare con le macchinine, che usa i led al posto delle automobiline (hai mai visto Tron 😀 ?)

GETTING STARTED

Cosa serve:

  • Arduino UNO o simili;
  • Strip led WS2812B: almeno 5m, 60 led/metro;
  • Alimentatore 5V > 2A: più la striscia è lunga, più led ci sono, più l’alimentatore dovrà essere potente 🤟;
  • 4 arcade button;
  • speaker 8 Ohm 2W (ma va bene anche un 4 Ohm);
  • resistenza da 1k e condensatore elettrolitico da 2.2 uF.

Primo circuito

In fritz non ci sono gli arcade button, quindi ho usato quelli normali, ma il concetto è lo stesso🤓.

Attenzione alla polarità del condensatore 🙃. Per il resto non c’è un gran chè da capire.

CAPIAMO IL CODICE

Nel codice sono spiegati a grandi linee i concetti fondamentali. Qui di seguito andiamo un po’ più a fondo per capire meglio alcuni punti.

set_ramp e set_loop – Rampa e giro della morte

void set_ramp(byte ramp_high, byte ramp_startl, byte ramp_centerl, byte ramp_endl)
{
    float k;

    k = (float)ramp_high / (ramp_centerl - ramp_startl);
    // Set the hill leds
    for (int i = 0; i < (ramp_centerl - ramp_startl); i++)
    {
        gravity_map[ramp_startl + i] = 127 - i * k;
    }
    gravity_map[ramp_centerl] = 127;

    k = (float)ramp_high / (ramp_endl - ramp_centerl);
    // Set downhill leds
    for (int i = 0; i < (ramp_endl - ramp_centerl); i++)
    {
        gravity_map[ramp_centerl + i + 1] = 127 + ramp_high - i * k;
    }
}

Qui è importante capire cos’è gravity_map (definito un po’ più su): prima di tutto è un array di byte di lunghezza dato dal numero dei led che formano la mia striscia, il cui valore di default per ogni cella è 127 (la metà di 255 arrotondata per difetto, arriveremo a capire il perchè). Ma a cosa serve? semplicemente a inserire punti di discesa e di salita 😀. In questo modo nei punti di salita sarà necessario premere più velocemente per mantenere la velocità costante, mentre nei punti di discesa avremo, a parità di click del tasto, una velocità maggiore.

Ora, set_ramp come dice il nome crea una rampa dell’altezza e lunghezza data in input. Vediamo di capire con un esempio: supponiamo di voler creare una rampa tra i led 100 e 110 (quindi lunga 10 led)molto ripida e simmetrica; la funzione richiede come input ramp_high, cioè l’altezza della rampa e le posizioni dei led di partenza, di centro rampa e di arrivo. Quindi nel nostro esempio, ramp_high = 20, ramp_startl = 100, ramp_centerl = 105, ramp_endl = 110.

Per prima cosa ci calcoliamo il fattore di pendenza k = 5, dopodichè calcoliamo il nuovo valore di gravità per la nostra salita (che va dal led 100 al led 105); avremo:

gravity_map[100] = 127;
gravity_map[101] = 122;
gravity_map[102] = 117;
gravity_map[104] = 112;
gravity_map[105] = 107;

In maniera analoga per il tratto di discesa (che va dal led 105 al led 110):

gravity_map[106] = 147;
gravity_map[107] = 142;
gravity_map[108] = 137;
gravity_map[109] = 132;
gravity_map[110] = 127;

Per la funzione set_loop il concetto è lo stesso (attenzione però: nel led centrale del cerchio della morte gravity_map = 255, mentre per la rampa gravity_map = 127; pensaci 😉).

Come varia la velocità durante la gara

Vediamo ora come vengono usati i valori settati in gravity_map durante la gara.

for (int i = 0; i < MAX_PLAYERS; i++)
{
    if((flag_sws[i] == 1) && (digitalRead(PIN[i]) == 0))
    {
        flag_sws[i] = 0;
        speeds[i] += ACEL;
    }
    if ((flag_sws[i] == 0) && (digitalRead(PIN[i]) == 1))
    {
        flag_sws[i] = 1;
    }
    if ((gravity_map[(word)dists[i] % NPIXELS]) < 127)
        speeds[i] -= g_force * (127 - (gravity_map[(word)dists[i] % NPIXELS]));
    if ((gravity_map[(word)dists[i] % NPIXELS]) > 127)
        speeds[i] += g_force * ((gravity_map[(word)dists[i] % NPIXELS]) - 127);

    speeds[i] -= speeds[i] * friction;
    dists[i] += speeds[i];
}

Prendiamo in considerazione il player-1 (quindi i = 0) alla partenza: se il tasto sarà premuto la sua velocità (speed[0]) sarà incrementata del valore di ACEL (nel nostro caso 0.2). Supponendo partenza da fermo sarà speed[0] = 0.2; a questo punto il valore di dists[0] % NPIXELS  sarà 0 (dists[0] = 0, il modulo prende il resto della divisione tra dists[0] e NPIXEL = 300). Siccome gravity_map[0] = 127 la velocità acquisita diminuirà del solo valore di speed[0]*friction= 0.1976 (friction = 0.012). Risulta ovvio come per mantenere la velocità o accelerare dobbiamo premere il tasto il più possibile. 

Bene, arriviamo alla fatidica salita: dists[0]= 101 e quindi dists[0] % NPIXELS  = 101 ma gravity_map[101] < 127 e quindi la velocità viene decrementata anche di un valore pari a g_force * (127 - 122) = 0.02 oltre che del valore speed[0]*friction. 

Per intenderci, se siamo arrivati all’inizio della salita con un valore di speed[0] = 10, nel primo led della salita la velocità diventa speed[0] = 10 - 0,02 - 10*0.012 = 9.68; se mantengo il numero di click costante, la speed al giro dopo sarà 9.68 + 0.02 < 10 (speed precedente). Per mantenere la velocità costante, il player 1 dovrà aumentare il numero di click 🤓.

Per la discesa il concetto si inverte, e quindi un numero maggiore di click porterà ad un’accelerazione.

Una cosa strana

Un piccolo sguardo a una cosa che difficilmente si vede in uno sketch Arduino:

void (*draw_cars[MAX_PLAYERS])(void);

Cos’è? Sicuramente un array, ma con una caratteristica particolare: è un array di puntatori a funzioni.

Nel linguaggio C/C++ (e quindi anche nel wiring, loro diretto discendente) è possibile utilizzare i puntatori oltre che per le variabili (se non sai cosa sono, wikipedia è tua amica 😉) anche per le funzioni, anch’esse allocate in indirizzi di memoria a cui un puntatore può essere assegnato.

Ho utilizzato questa feature speciale per due motivi: 

  1. Posso usare un loop per disegnare i led che rappresentano le varie macchine sulla pista;
  2. Aumenta (seppur di poco) la velocità di esecuzione della porzione di codice in cui la utilizzo.

Il bug nascosto

C’è un piccolo bug nascosto all’interno del codice, proprio nel punto dove è necessario disegnare i led che rappresentano le macchinine. Trovalo e migliora il codice 😃

LA REALIZZAZIONE DOPO LE PROVE

una volta testato su Breadboard, bisogna assemblare il tutto e dargli una forma. Ecco la lista della spesa:

  1. Connettori RCA femmina (link);
  2. Cavi RCA maschio-maschio (link);
  3. Joystick e alloggiamento Arduino stampati in 3D (li trovi nel link github);
  4. Strip Led 5m 60 led/metro;
  5. Materiale riciclato all’interno del FabLab.

link github https://github.com/CCorsett/LedRace