Pagina1 van 1
Forum

Welkom bij de Tweaking4All gemeenschapsforums!
Voor je gaat deelnemen, bekijk de Forum Regels!

Specifieke onderwerpen: Start het onderwerp met de naam van het programma of systeem.
Bijvoorbeeld “MacOS X – Jouw vraag“, of bijvoorbeeld “MS Word – Jouw Tip of Truc“.

Merk op: Omschakelen naar een andere taal zal niet werken als je een post aan het lezen bent aangezien er waarschijnlijk geen vertaling beschikbaar is.



Arduino scorebord N...
 
Deel:
Meldingen
Alles wissen

Arduino scorebord Neopixel, onbegrijpelijk stukje code.

87 Berichten
2 Gebruikers
0 Vind-ik-leuks
2,488 Bekeken
 Hans
(@hans)
Famed Member Admin
Deelgenomen: 11 jaar geleden
Berichten: 2677
 

Het is goed om dat soort ideeën vooraf te definiëren nu het nog kan.

En hier mijn Zondagmiddag babbels ...

Ik probeer ook altijd m'n code zo te schrijven dat aanpassen eenvoudig blijft 😊 
Dat gaat het beste door alle "problemen" op te splitsen in deelproblemen welke elk een elegante en misschien zelfs universele oplossing hebben.
Misschien lastig als ik het zo zeg, maar als we klaar zijn weet ik zeker dat je 'm helemaal ziet 😁 

Wat soldeerwerk betreft: als het maar consistent is, dan passen we de code zonder moeite aan (b.v. score thuis en bezoekers moeten dezelfde layout hebben). 😊 

Als ik naar jouw eerste layout kijk:

- Tijd 
- Fouten thuis, Fouten uit
- Periode
- Score thuis, Score uit

Voor de nummers, gebruik je consistent "F A B G E D C" (met uitzondering van het eerste nummer bij de score natuurlijk) in jouw soldeerwerk?
"A B C D E F G" had mooier geweest (soort van standard voor segment LED displays), maar we kunnen daarvoor zelfs een vertaal tabel maken.

Als voorbeeld:

"A B C ...":  A = LEDs 0, 1, 2, ... ,7 ( 8 LEDs )

maar in jouw soldeerwerk:

"F A B ...": A = LEDs 8, 9, 10, ... ,15 ( 8 LEDs )

 

Zo kunnen we dan in de code altijd het "A" segment aanspreken via een variable of constante ...

Als ik dan naar de score nummers kijk, dan weten we ook hoe groot ieder segment is ( 8 LEDs ). Maar ik kan me voorstellen dat een verticale lijn minder LEDs kan bevatten dan een horizontale lijn in andere designs. Iets waar ik in m'n code dan weer rekening zou houden omdat het een bekend probleem is, wat zich zelfs op een computerscherm kan voordoen.

Nu zie ik ook dat tijd meer LEDs per segment lijkt te gaan gebruiken? En is dit hetzelfde als het periode-nummer?
(ik wil al die info variabel houden zodat we straks ook andere designs met dezelfde code kunnen maken)

Dan was er nog bediening;

Optie 1 = Bluetooth

Op zich niks mis mee, moet er zelf nog wel even mee spelen. Maar BT heeft als nadeel dat je een geschikte applicatie op jouw telefoon nodig hebt (je was er al met een bezig als ik me goed herinner). Ik zou dan ook een app kiezen die voor zowel Android als iOS beschikbaar is - mocht het ooit nodig zijn.
Dan is er per app nog hoe de knoppen configuratie wordt opgezet - dit heb ik zelf nog nooit gedaan, dus ik weet niet of je dat specifiek als template op de telefoon op slaat of dat de Arduino/ESP deze info levert.

De app, als het eenmaal draait, kan het gemakkelijkste werken, maar je blijft wel afhankelijk van de betreffende app. Dus als de app niet meer beschikbaar is, dan hebben we gewoon pech. In praktijk zal zo'n app wel een jaar of 2 - 3 zeker nog beschikbaar zijn.

Optie 2 = WiFi

Hierbij hebben we dan 3 opties:
- waarbij de ESP32 zelf een WiFi Access Point is (en je dus in jouw telefoon daar contact mee kunt zoeken), 
- de ESP32 zich aan het hotspot van jouw telefoon koppelt (kan wat lastiger zijn, en misschien ook niet wenselijk),
- de ESP32 hangt zich in een lokaal WiFi network (lastig als je niet thuis speelt).

Bij al deze opties heb je wel het voordeel dat besturing via web pagina's op de ESP32 kan worden gedaan. Dus geen speciale app nodig en bestuurbaar via elke browser op welk apparaat dan ook (telefoon, tablet, PC, etc). Je bent dus nooit afhankelijk van derden.

Het leveren van webpagina's kan echter alleen maar over WiFi omdat BT dat niet ondersteund (zegt men, hoewel er wel browsers zijn die dit wel ondersteunen - roept dus om wat testjes 😊 ).


   
BeantwoordenCiteren
(@Anoniem)
Deelgenomen: 1 seconde geleden
Berichten: 0
Topic starter  
Geplaatst door: @hans

Misschien lastig als ik het zo zeg, maar als we klaar zijn weet ik zeker dat je 'm helemaal ziet

Moet hem even 3x lezen, maar dat zal wel goed komen 😉 

Geplaatst door: @hans

Voor de nummers, gebruik je consistent "F A B G E D C"

Ja en dat gebruik ik inderdaad overal, de honderdtallen hebben ook 8leds per segment (dus 16)

Geplaatst door: @hans

Nu zie ik ook dat tijd meer LEDs per segment lijkt te gaan gebruiken? En is dit hetzelfde als het periode-nummer?

Nee, ze gebruiken allemaal 8 LEDs per segment. Enkel de dubbele punt gebruikt 2x 2 LEDs. Ook de fouten gebruiken steeds 5x 2 LEDs.

 

Qua bluetooth en wifi : Het idee was denk ik iets te enthousiast.... ik was inderdaad bezig met het maken van een app (MIT app inventor), maar dat gaat eigenlijk pas als alle code af is EN alles in de code is voorverwerkt. Op zijn zachts gezegd lastig 🤔 

Wat voor nu (denk ik) het beste is, om er een fysieke knoppenkast aan te maken. Op die manier zit er in ieder geval geen vertraging in als er bijvoorbeeld een time-out aangevraagd wordt (dan moet de tijd meteen pauzeren) Hiervoor kunnen we de (vele) pin-outs van de ESP gebruiken dacht ik. Daarom stelde ik ook voor om eventueel de verschillende digits (tijd compleet, punten links, punten rechts, fouten links, fouten rechts, periode) allemaal apart aan te sluiten op de ESP. Lijkt me makkelijker voor de code...

Ik ga me wel even een klein beetje verdiepen in je wifi verhaal, lijkt me zeker wel interessant !


   
BeantwoordenCiteren
(@Anoniem)
Deelgenomen: 1 seconde geleden
Berichten: 0
Topic starter  

Inmiddels heb ik de plaat uitgefreesd. De behuizingen liggen nu met de bovenkant gelijk met de plaat, dan kan er evt. nog een plexiglas plaat over achteraf.

Het solderen van de led's is nogal een friemelkarwei, maar de tijd-aanduiding is af. Mooi, want mijn draad is ook op 😆 . Even nieuwe regelen.

Voorbeeldsketch op de arduino gezet om alles even te testen :

 

 

Scorebord :

 

Wat betreft de instellingen van de tijd (time-out, pauze, aftellen voor wedstrijd enz.) wacht ik nog af wat de precieze bedoelingen zijn....


   
BeantwoordenCiteren
 Hans
(@hans)
Famed Member Admin
Deelgenomen: 11 jaar geleden
Berichten: 2677
 

Dat ziet er al strak uit!
Ik probeer morgen een stukje van de code te gaan doen: het besturen van een nummer, en het zetten van tijd, score etc.


   
BeantwoordenCiteren
(@Anoniem)
Deelgenomen: 1 seconde geleden
Berichten: 0
Topic starter  

Heb inmiddels weer spullen om verder te solderen. Ook een workflow gekregen v.w.b. extra instellingen die we uiteindelijk graag zien, maar dat kan later uiteraard.


   
BeantwoordenCiteren
(@Anoniem)
Deelgenomen: 1 seconde geleden
Berichten: 0
Topic starter  
Geplaatst door: @fredrossi

Ik hoop dat het zo wat begrijpelijker wordt. Blijken uiteindelijk "maar" 544 te zijn 🤐 

 

Zo krijg je misschien een idee van wat het gaat worden:

Telvoutje gemaakt met het aantal leds. Qua berekening kwam ik uit op 560, maar had dat niet goed bekeken in de tekening. Daar staan er maar 544 op.... Even een nieuwe gemaakt.

 

Nu ook alles aan elkaar gesoldeerd, dus tijd voor een totaal testje. Ingesteld op 20 procent, maar omdat het donker was lijkt het uiteraard feller te zijn dan het is.


   
BeantwoordenCiteren
 Hans
(@hans)
Famed Member Admin
Deelgenomen: 11 jaar geleden
Berichten: 2677
 

Het heeft even geduurd - zorg voor m'n moeder neemt toch stiekem meer tijd in beslag dan verwacht, maar gelukkig deze week was een goede vooruitgang voor haar.

Als eerste de basisopstelling voor de LED strip, zoals we die in andere sketches ook zien:

#define FASTLED_INTERNAL // just used to mute the Pragma messages when compiling
#include "FastLED.h"

// LED details (aanpassen indien nodig)
#define NUM_LEDS 60     // Number of LEDs in strip
#define LED_PIN 4       // Pin we used for the strip
#define LED_TYPE WS2812 // Type of LED strip
#define COLOR_ORDER GRB // Color order LED strip
#define BRIGHTNESS 64   // Overall brightness of the entire strip

CRGB leds[NUM_LEDS];    // LED strip array

void setup() {
  // ....

  // Initialize LED strip
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  FastLED.setBrightness( BRIGHTNESS ); // Set overall brightness
  FastLED.clear(true);                 // clear LEDs and Show

  // ....
}

 

Vervolgens wil ik graag het probleem opbreken in een kleinere problemen.
Ik zie voor de weergave (Bluetooth enzo nog even niet meegerekend) een aantal basisfuncties:

  • Speeltijd zetten
  • Periode zetten
  • Score zetten (thuis, uit)
  • Fout telling zetten (thuis, uit)

 

Voor de eerste 3 hebben we een functie nodig die nummers zet, voor de 4de hebben we een functie nodig die de punten zet.

Ik begin even met de fouten tellen. Hiervoor hebben we:

  • een start punt (eerst LED voor de reeks thuis of uit)
  • punt grootte (aantal leds per "dot" - 2)
  • aantal fouten dat geteld kan worden (5)
  • punt kleuren (een kleur per dot)
  • Of de telling van links naar rechts of van rechts gaat

Dit ziet er wat breder uit dan wat gepland is, maar nu zijn we wel lekker flexibel.
We kunnen dus kleuren veranderen en richting veranderen indien wenselijk.

Ik dacht zelf een aantal van deze instellingen als "defines" in te stellen.

// Fail counters
#define FailHomeStartLED      10    // First LED for the Home fail counter
#define FailGuestsStartLED    20    // First LED for the Guests fail counter

#define FailLEDsPerDot        2     // Number of LEDs in a fail dot
#define FailDotCount          5     // Number of fails/dots for Home or Guests

#define FailHomeLeftToRight   true  // Home: true = from left to right, false = from right to left
#define FailGuestsLeftToRight true  // Guests: true = from left to right, false = from right to left

// Fail dot colors
CRGB FailColors[FailDotCount] = { CRGB(255,255,0), CRGB(255,200,0),  CRGB(255,150,0),  CRGB(255,100,0),  CRGB(255,0,0) };

// or use CRGB names;
// CRGB FailColors[FailDotCount] = { CRGB::Yellow, CRGB::Yellow, CRGB::Yellow, CRGB::Orange, CRGB::Red };

 

We geven dus een startpositie op (FailHomeStartLED en FailGuestsStartLED), zodat we weten waar de eerste "fout" punt start.
Let dus wel op, als je besluit van rechts naar links te gaan, dan is het eerste LED aan de andere kant.
Bijvoorbeeld, uitgaande van 5 fouten ieder en elke van de punten bestaande uit 2 LEDs (dus 10 LEDs per team):

Home start bij 0 en gasten bij 10 als beiden van links naar rechts gaan.
Home start bij 0 en gasten bij 20 als je home van links naar rechts doet en gasten van rechts naar links.

Vervolgens definiëren we hoeveel LEDs we gebruiken per punt (FailLEDsPerDot = 2) en hoeveel fouten (FailDotCount = 5) er maximaal gemaakt kunnen worden.

Daarnaast geven we ook nog aan of we van links naar rechts, of rechts naar link, willen gaan (FailHomeLeftToRight en FailGuestsLeftToRight -- true = links naar rechts, false = rechts naar links). Het idee daarbij was dat Home links begint te tellen, en Guests rechts, en beiden naar het midden bewegen. Of dat we alle twee gewoon standaard van links naar rechts werken.

En als laatste, welke kleuren we willen gebruiken (array FailColors) - dit kunnen allemaal dezelfde kleuren zijn, maar het kan ook b.v. van geel naar rood verlopen, of net wat je in gedachten hebt.

Dit heb ik dan allemaal bij elkaar gezet in een functie (SetFail(Thuis,Gasten);) die de fouten zet voor Home en Guest teams.

Zoals je ziet ben ik hier lekker overboard gegaan, maar dat maakt het e.e.a. wel lekker flexibel.

Alles bij elkaar voor de fouten tellen kan er dan zo uit zien:

#include <FastLED.h>

#define LED_PIN     5
#define NUM_LEDS    50
#define BRIGHTNESS  64
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];

// Fail counters
#define FailHomeStartLED      10     // First LED for the Home fail counter
#define FailGuestsStartLED    20    // First LED for the Guests fail counter

#define FailHomeLeftToRight   true  // Home: true = from left to right, false = from right to left
#define FailGuestsLeftToRight true  // Guests: true = from left to right, false = from right to left

#define FailLEDsPerDot        2     // Number of LEDs in a fail dot
#define FailDotCount          5     // Number of fails/dots for Home or Guests

// Fail dot colors
CRGB FailColors[FailDotCount] = { CRGB(255,255,0), CRGB(255,200,0),  CRGB(255,150,0),  CRGB(255,100,0),  CRGB(255,0,0) };

// or use CRGB names;
// CRGB FailColors[FailDotCount] = { CRGB::Yellow, CRGB::Yellow, CRGB::Yellow, CRGB::Orange, CRGB::Red };

void setup() {
    FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
    FastLED.setBrightness(  BRIGHTNESS );      // Set overall brightness
    FastLED.clear(true);                       // clear LEDs and Show
}


void loop()
{
   SetFail(3,4); // zet de foutelling voor Home (3) en Guests (4) 
}

void SetFail(int FailCountHome, int FailCountGuests) {
  int Direction;

  FailHomeLeftToRight ? Direction = 1 : Direction = -1;
  
  for(int FailCounter=0; FailCounter<FailCountHome; FailCounter++) {
    for(int LEDsInDot=0; LEDsInDot<FailLEDsPerDot; LEDsInDot++) {
      leds[FailHomeStartLED + Direction*((FailCounter*FailLEDsPerDot) + LEDsInDot)] = FailColors[FailCounter];
    }
  }
  
  FailGuestsLeftToRight ? Direction = 1 : Direction = -1;
  
  for(int FailCounter=0; FailCounter<FailCountGuests; FailCounter++) {
    for(int LEDsInDot=0; LEDsInDot<FailLEDsPerDot; LEDsInDot++) {
      leds[FailGuestsStartLED + Direction*((FailCounter*FailLEDsPerDot) + LEDsInDot)] = FailColors[FailCounter];
    }
  }

  FastLED.show(); 
}

 

Ik kan het niet testen met hardware, omdat ik het e.e.a. niet hier heb liggen natuurlijk. Maar ik kon de code wel testen WokWi (Arduino simulator).
De sketch hierboven heb ik in WokWi gezet, zie hier als je even met de code wilt prullen.

Mocht je het met jouw hardware willen proberen dan moet je even de LED strip info aanpassen, en minstens de startpunt aangeven (richting, kleuren, etc - daar kun je naar hartelust mee spelen).


   
BeantwoordenCiteren
(@Anoniem)
Deelgenomen: 1 seconde geleden
Berichten: 0
Topic starter  

Dankjewel!! Super gedaan!

Ben er mee aan de slag gegaan. Ik maakte in eerste instantie de fout dat ik telde vanaf 1. Moet natuurlijk vanaf 0 zijn, dus de eerste led van fouten thuis is nr 256, en niet 257..... evenzo met de gasten. Wat ik in eerste instantie niet werkend kreeg, was het aantal leds, voornamelijk van de thuiskant. Door SetFail aan te passen naar (5,5) gaat het allemaal branden. Daar neem ik aan dat er dus een knop aan toegekend wordt o.i.d.

Over die knoppen : het is ons inmiddels wel duidelijk dat we geen gebruik gaan maken van wifi of bluetooth. We gaan voor een directe kabelverbinding naar verschillende knoppen. Dit om het eventueel wegvallen van de verbinding (en dus het exact bedienen van bijvoorbeeld de tijd) te voorkomen. Better safe then sorry....

Heb je graag dat ik een schema meegeef over hoe de knoppen uiteindelijk het bord aan zouden moeten sturen? Het is wel allemaal specifiek, maar zoals gezegd, als we toch bezig zijn kan het maar beter in 1x goed toch?


   
BeantwoordenCiteren
 Hans
(@hans)
Famed Member Admin
Deelgenomen: 11 jaar geleden
Berichten: 2677
 

Normaal bedenk ik de functies eerst die bepaalde taken uitvoeren (weergave van de fouten b.v.) en die te testen door de functie aan te roepen of met knoppen.
Als alles dan werkt, was het idee om dan de "schakelaar" via Bluetooth of WiFi te doen. Gewone knoppen is ook prima 😉 

SetFail(5,5) was inderdaad een test van de SetFail functie. Straks houden we een teller bij voor UIT en THUIS, die we dan beïnvloeden met een knop of iets dergelijks en na verandering roepen we dan de functie SetFail aan.

Wat de werking en aansluiting van de knoppen betreft: dat is nu nog niet zo belangrijk.
Het opvangen van een knop indrukken heeft zo z'n eigen uitdagingen 😁  (maar daar vinden we wel wat op met een interrupt).

Volgende stap is dus een functie om nummers weer te geven voor Tijd Minuten (00-59), Tijd Seconden (00-59), Periode (0-9) en Score Uit (0-199) en Score Thuis (0-199).
En voor elk van die functies hebben we dus een logica nodig voor het opbouwen van een nummer (wat dus ook een functie kan worden).
Daar ga ik nu naar kijken 😊 


   
BeantwoordenCiteren
 Hans
(@hans)
Famed Member Admin
Deelgenomen: 11 jaar geleden
Berichten: 2677
 

OK, deze stap wordt wat complexer - even wat zaken definiëren ...

Als eerst willen we de configuratie van een nummer flexibel houden. Onder normale omstandigheden hebben we een segment volgorde van ABCDEFG.
Note: Ik laat segment 8 (de punt) vallen omdat we die alleen in de tijd nodig hebben.

Maar soldeer technisch heb jij het dus iets anders gedaan (FABGEDCH).
Geen probleem, maar ik wil de code wel zo houden dat er op een later moment iemand de "standaard" volgorde wel kan hanteren (ABCDEFG).

Ik ga er ook vanuit dat alle nummers op dezelfde manier zijn geconfigureerd. Dus zelfde segment volgorde, even groot, etc.

Als we uitgaan van een enkel nummer (beginnen bij de basis), dan moeten we dus weten waar een segment zit, hoe groot het is en of het gebruikt wordt.
Hierbij kunnen we wat rekenen om het e.e.a. eenvoudiger/flexibeler te houden.

Dus als eerste heb ik de de standaard start punten van segmenten van een nummer gedefinieerd.
Het idee daarbij is dat we een startpunt van een nummer kunnen pakken en deze waarden er bij optellen om te bepalen waar een specifiek segment begint.

Bij een normale volgorde zou dat dit zijn:

#define SegmentA 0
#define SegmentB 8
#define SegmentC 16
#define SegmentD 24
#define SegmentE 32
#define SegmentF 40
#define SegmentG 48
#define SegmentB 56

 

Maar door de afwijkende volgorde wordt het dus:

#define SegmentA 8
#define SegmentB 16
#define SegmentC 48
#define SegmentD 40
#define SegmentE 32
#define SegmentF 0
#define SegmentG 24
#define SegmentB 56

 

Misschien een beetje uitleg bij nodig; we beginnen dus bij segment F, dus die start bij LED 0.
Het volgende segment is A, en die begint dus bij het 8ste LED.
B begint bij het 16e LED.
G bij het 24e LED. 
etc.

Daarnaast definieer ik een aparte breedte en hoogte van een segment, zodat we flexibel zijn in hoe we een nummer bouwen.
Ik ga even uit van de waarden die je eerder noemde, dus elk segment is 8 LEDs hoog of 8 LEDs breed.

#define SegmentHSize 8
#define SegmentVSize 8 

 

Op basis van deze gegevens kunnen we ook berekenen hoeveel LEDs een nummer in beslag neemt:

#define TotalSegmentSize  (3*SegmentHSize)+(4*SegmentVSize)

 

We hebben 3 horizontale segmenten en 4 verticale segmenten - en dit getal wil ik gebruiken om dynamisch te kunnen bepalen waar een volgend nummer gaat staan.

Ik definieer ook meer even de twee dubbele punten voor de tijd als 4 LEDs, maar ook die kunnen we dus veranderen.

#define TimeSeparatorSize 4   // 2 dots, each 2 LEDS

 

Uitgaande van een bedrading die zo loopt, kunnen we eigenlijk de meeste dingen berekenen.
Volgorde: Tijd minuten -> Tijd dubbele punt -> Tijd Seconden -> Fouls Home -> Period -> Foul Guests -> Score Home -> Score Guests

// Assume wiring: 
// Start at time minutes -> time colon -> seconds -> Fouls Home -> Period -> Foul Guests -> Score Home -> Score Guests
 
#define StartLedMinutes10        0
#define StartLedMinutes1         TotalSegmentSize
#define StartLedSecondsSeparator StartLedMinutes1 + TotalSegmentSize
#define StartLedSeconds10        StartLedSecondsSeparator + TimeSeparatorSize
#define StartLedSeconds1         StartLedSeconds10 + TotalSegmentSize
                          
#define StartLedPeriod           FailHomeStartLED + (FailDotCount*FailLEDsPerDot) 

#define StartLedScoreHome100     FailGuestsStartLED + (FailDotCount*FailLEDsPerDot)
#define StartLedScoreHome10      StartLedScoreHome100 + (2*SegmentVSize)
#define StartLedScoreHome1       StartLedScoreHome10 + TotalSegmentSize

#define StartLedScoreGuests100   StartLedScoreHome1 + (FailDotCount*FailLEDsPerDot)
#define StartLedScoreGuests10    StartLedScoreGuests100 + (2*SegmentVSize)
#define StartLedScoreGuests1     StartLedScoreGuests10 + TotalSegmentSize

 

Hier bepalen we dus "dynamisch" waar de tientallen van de minuten beginnen (StartLedMinutes10), de eentallen (StartLedMinutes1), de dubbele punt (StartLedMinutes10), de tientallen van de seconden (StartLedSeconds10) en de eentallen (StartLedSeconds1).

Je ziet dat ik al een stukje uit de vorige code heb meegenomen (FailHomeStartLED, FailDotCount, FailLEDsPerDot, FailGuestsStartLED).

Om nu vlot en gemakkelijk de segmenten te kiezen voor een specifiek nummer, dacht ik aan een multidimensionale array, waarin we aangeven met "1" en "0" of een segment nodig is. Gebaseerd op deze segmenten - onafhankelijk van hoe ze aangesloten zijn:

Segmenten voor nummers:

0 ABCDEF
  1111110
1  BC
  0110000
2 AB DE G
  1101101
3 ABCD G
  1111001
4  BC FG
  0110011
5 A CD FG
  1011011
6 A CDEFG
  1011111
7 ABC
  1110000
8 ABCDEFG
  1111111
9 ABCD FG
  1111011

 

Ik hoop dit een beetje logisch klinkt. Het idee is dat als we de array kunnen aanspreken met een eerste index - het nummer wat we weer willen geven (0,1,2,...9). Met de tweede index kunnen we dan zien of een segment nodig is of niet.
Ik weet het - dit kan verwarrend zijn, en dat is het soms voor mij ook, dus het kan heel goed zijn dat ik straks iets moet herzien omdat ik ook even de mist in ga 😉 

Zo'n array ziet er dan zo uit:

//    Segments orientation: H V V H V V H                         
//           Segments used: A B C D E F G      For number:
byte SegmentNumbers[10][7] = { { 1,1,1,1,1,1,0 },   // 0
                               { 0,1,1,0,0,0,0 },   // 1
                               { 1,1,0,1,1,0,1 },   // 2
                               { 1,1,1,1,0,0,1 },   // 3
                               { 0,1,1,0,0,1,1 },   // 4
                               { 1,0,1,1,0,1,1 },   // 5
                               { 1,0,1,1,1,1,1 },   // 6
                               { 1,1,1,0,0,0,0 },   // 7
                               { 1,1,1,1,1,1,1 },   // 8
                               { 1,1,1,1,0,1,1 } }; // 9

 

 Dus als we het nummer "1" willen weergeven, dan kijken we naar deze array als volgt:

SegmentNumber[1][x]

waarbij "x" het segment A, B, C, ... , G is. Mocht de array een "1" terug geven dan moeten we een segment weergeven. Is het een "0" dan dus niet.

Nogmaals: doordat we met define's de start posities van "SegmentA" enzo hebben bepaald, maakt nu de soldeer volgorde niet meer uit. De "define's" vertalen het voor ons.


   
BeantwoordenCiteren
 Hans
(@hans)
Famed Member Admin
Deelgenomen: 11 jaar geleden
Berichten: 2677
 

OK (even gepost om het voor mij ook leesbaar te houden) ...

Op basis van deze gegevens, kunnen we nu een nummer weergeven, maar we hebben eerst nog een paar functies nodig.

  • Een functie om een nummer helemaal uit te zetten
  • Een functie om een segment aan te zetten
  • Een functie om de benodigde segmenten voor een nummer aan te zetten

 

SetDigitDark - het nummer helemaal uit zetten.
Deze is relatief eenvoudig als we de positie van het eerste LED weten.

void SetDigitDark(int StartLED) {
  for(int i=StartLED; i<( StartLED+(3*SegmentHSize)+(4*SegmentVSize) );i++) { // Number has 3 Horizontal and 4 vertical segments
    leds[StartLED+i] = CRGB::Black;
  } 
}

 

We tellen vanaf StartLED tot StartLED+(Alle LEDs voor de segmenten van dit nummer). Voor elke "leds[]" zetten we de kleur op zwart.
Je zou hierna een FastLED.show() kunnen doen om dat meteen zichtbaar te maken, maar dat doen we nog niet om 2 redenen:

  1. We hebben nog meer LEDs die aangepast moeten worden
  2. Om geknipper te voorkomen, als bepaalde segmenten toch aan moeten staan.

 

SetSegment - Een functie om individuele segmenten aan te kunnen zetten.
Klinkt spannend maar is het helemaal niet haha ...

void SetSegment(int StartLED, int LEDCount, CRGB SegmentColor) {
  for(int i=0; i<LEDCount; i++) {
    leds[StartLED+i] =SegmentColor;
  }
}

 

Het enige wat we hier doen is een aantal LEDs (LEDCount) aan te zetten (SegmentColor), met StartLED als start punt.
Welke segment dat is, is iets wat we in de volgende functie pas bepalen.

 

SetDigit - met deze functie zetten we de benodigde segmenten voor een enkel nummer (0-9) aan.

Als eerste zetten we alle segmenten op zwart (zonder dit meteen zichtbaar te maken).
Daarna zetten we de segmenten aan die we nodig hebben.

In de code hieronder:

void SetDigit(int StartLED, CRGB DigitColor, int DigitValue) {
  SetDigitDark(StartLED); // set LED to dark, do not make this change visible yet (avoid flicker)
  
  if( SegmentNumbers[DigitValue][0]==1 ) { SetSegment(StartLED+SegmentA, SegmentHSize, DigitColor); } // A = horizontal
  if( SegmentNumbers[DigitValue][1]==1 ) { SetSegment(StartLED+SegmentB, SegmentVSize, DigitColor); } // B = vertical
  if( SegmentNumbers[DigitValue][2]==1 ) { SetSegment(StartLED+SegmentC, SegmentVSize, DigitColor); } // C = vertical
  if( SegmentNumbers[DigitValue][3]==1 ) { SetSegment(StartLED+SegmentD, SegmentHSize, DigitColor); } // D = horizontal
  if( SegmentNumbers[DigitValue][4]==1 ) { SetSegment(StartLED+SegmentE, SegmentVSize, DigitColor); } // E = vertical
  if( SegmentNumbers[DigitValue][5]==1 ) { SetSegment(StartLED+SegmentF, SegmentVSize, DigitColor); } // F = vertical
  if( SegmentNumbers[DigitValue][6]==1 ) { SetSegment(StartLED+SegmentG, SegmentHSize, DigitColor); } // G = horizontal
} 

 

Stel we zeggen SetDigit(0, CRGB::Red, 1);

Dan zien we dus eerst dat met SetDigitDark het nummer uitgezet wordt (nog niet zichtbaar).
Vervolgens gaan we array doorlopen.

Als SegmentNumbers[1][0] de waarde "1" bevat, dan moeten we het A segment aanzetten. Niet het geval, dus overslaan.
Als SegmentNumbers[1][1] de waarde "1" bevat, dan moeten we het B segment aanzetten. Wel het geval, dus met SetSegment Segment "B" op rood (nog niet zichtbaar).
Als SegmentNumbers[1][2] de waarde "1" bevat, dan moeten we het C segment aanzetten. Wel het geval, dus met SetSegment Segment "C" op rood (nog niet zichtbaar).
Als SegmentNumbers[1][3] de waarde "1" bevat, dan moeten we het D segment aanzetten. Niet het geval, dus overslaan.
Als SegmentNumbers[1][4] de waarde "1" bevat, dan moeten we het E segment aanzetten. Niet het geval, dus overslaan.
Als SegmentNumbers[1][5] de waarde "1" bevat, dan moeten we het F segment aanzetten. Niet het geval, dus overslaan.
Als SegmentNumbers[1][6] de waarde "1" bevat, dan moeten we het G segment aanzetten. Niet het geval, dus overslaan.

Na het aanroepen van deze functie zouden we ook weet FastLED.show kunnen doen, maar we zijn nog niet klaar en we willen maximale performance en geen geknipper.


   
BeantwoordenCiteren
 Hans
(@hans)
Famed Member Admin
Deelgenomen: 11 jaar geleden
Berichten: 2677
 

Nu we nummers kunnen weergeven en gedefinieerd hebben waar deze nummers staan, kunnen we beginnen met het zetten van het eerste, en gemakkelijkste, nummer: De periode teller. Het liefste willen de dit lekker eenvoudig houden, dus ik dacht aan zoiets:

SetPeriod(StartLEDPeriod, CRGB::Green, 2);

 

We weten waar de periode teller staat (StartLEDPeriod), we willen het een kleur geven, en we moeten natuurlijk aangeven wat de periode is (2 als voorbeeld).

Nu zul je zien waarom het fijn is om bepaalde basisfuncties te definiëren ...

In onderstaande functie SetPeriod geven we het nummer weer in "PeriodColor", tenzij de period kleiner dan nul is, want dan staat het nummer gewoon uit (black).

void SetPeriod(int StartLED, CRGB PeriodColor, int Period) {
  if(Period<0) { 
    SetDigit(StartLED, CRGB::Black, Period);
  } else {
    SetDigit(StartLED, PeriodColor, Period);
  }  
}

 

Merk op: het bijhouden van score, tijd, fouten, etc doen we later in de "void loop" ... nu eerst kijken dat we informatie weer kunnen geven.


   
BeantwoordenCiteren
 Hans
(@hans)
Famed Member Admin
Deelgenomen: 11 jaar geleden
Berichten: 2677
 

🤞🏻 Ik hoop dat dit vorige stuk werkte - ik kan het namelijk moeilijk hier testen.

OK, we hebben fouls, en periods. Nu tijd voor weergave van de tijd (nog statisch op het moment).

Hiervoor maak ik eerste een kleine functie, SetTimeColon, om de dubbele punten aan te zetten naar gewenste kleur.
De kleur voeg ik toe omdat we soms een rode of groene kleur nodig hebben (of welke kleur dan ook).

void SetTimeColon(CRGB ColonColor) {
  for(int i=StartLEDSecondsSeparator; i<StartLEDSecondsSeparator+TimeSeparatorSize; i++) {
    leds[i]=ColonColor;
  }
}

 

Ik hoop dat deze functie nu ook voor de hand ligt.

De volgende functie, SetTime, moet minuten een seconden weergeven, en mogelijk ook de dubbele punt (indien die dezelfde kleur zou moeten zijn).

void SetTime(int Minutes, int Seconds, CRGB TimeColor) {
  int Tens;
  
  SetTimeColon(TimeColor);
  
  Tens = Minutes/10;
  SetDigit(StartLEDMinutes10, TimeColor, Tens);
  SetDigit(StartLEDMinutes1,  TimeColor, Minutes - (Tens*10) );
  
  Tens = Seconds/10;
  SetDigit(StartLEDSeconds10, TimeColor, Tens);
  SetDigit(StartLEDSeconds1,  TimeColor, Seconds - (Tens*10) );
}

 

Hier gebruiken we weer handig de weergaven functies van nummers 😁 
We moeten alleen wel even de nummers opsplitsen in 10-tallen en 1-tallen.

In dit geval doe ik dat door het nummer door 10 te delen en in een integer variabele (Tens) op te slaan.

Bij de Arduino is het zo dat als ik een deling doe en het resultaat op sla in een integer variabele, dan wordt alleen het integer deel opgeslagen.
Stel we delen 54 door 10, dan zou dit 5,4 leveren. Maar een integer variabele kan alleen maar gehele getallen (integers) opslaan, dus alleen "5" wordt opgeslagen.
Zie ook Data Types voor de Arduino en Arduino Division.

Het voordeel is dan dat we dus in de variabele "Tens" dus de tientallen hebben.
Maar ... we kunnen dit ook handig gebruiken om te bepalen wat de 1-tallen zijn.

Als reken voorbeeld:

Minutes = 54

Dan wordt Tens = 54 /10 = 5,4 naar een geheel getal = 5.

Dus voor de 10-tallen moeten we een "5" weergeven met onze SetDigit functie en de locatie van de tientallen voor minuten (StartLEDMinutes10).

Om de 1-tallen te bepalen, pakken we minuten en trekken daar 10 * Tens vanaf.
Dus 54 - ( 10 x 5 ) = 4.

Ook hier kunnen we met de SetDigit functie en de locatie van de 1-tallen voor minuten (StartLEDMinutes1) het juiste cijfer zetten.

 


   
BeantwoordenCiteren
 Hans
(@hans)
Famed Member Admin
Deelgenomen: 11 jaar geleden
Berichten: 2677
 

Nu moeten we alleen nog de scores weg zetten ... en hiervoor zou ik het liefste een functie zen die SetScore(thuis, gasten) kan doen.
Hierbij kan het meegeven van kleuren aardig zijn, dus die plak ik er ook even bij.

De weergave van een score nummer kan vergelijkbaar gedaan worden als we deden met de weergaven van minuten, maar we hebben een afwijking hier en dat zijn de 100-tallen.

De 100-tallen kent maar 2 waarden: "1" of "niet zichtbaar". Dus hiervoor heb ik een kleine aparte functie gemaakt voor dat eerste nummer:

void SetScore100(int StartLED, CRGB NumberColor, int Score) {
  if(Score<100) { NumberColor = CRGB::Black; } // do not show the 100 digit is score less than 100
  
  for(int i=0; i<(2*SegmentVSize); i++) {
    leds[StartLED+i] = NumberColor;
  }
}

 

Zoals we eerder zagen: ik gebruik steeds "StartLED" om de begin locatie van een nummer te bepalen, en zoals ik net zie: als de score kleiner dan 100 is, dan is dit nummer niet zichtbaar en dus een zwarte kleur. Als het nummer groter dan 99 is dan, dan wordt de gewenste kleur gebruikt.

Met een simpele for-loop passen we de kleur aan voor die 2 (verticale) segmenten.

Voor de andere nummers, 10-tallen en 1-tallen, passen we een soortgelijke truuk toe als bij de tijd.
Deze truuk passen we 2x toe: voor de thuis score en de gasten score, dus ik heb weer een aparte functie gemaakt zodat we dit gemakkelijk kunnen hergebruiken.

Om het generiek te houden, heb ik niet de vaste waarden voor de score nummers gebruikt. Die geven we door als parameters (zoals Start100, Start10, Start1 voor de nummer posities).

void SetScoreNumber(int Score, CRGB ScoreColor, int Start100, int Start10, int Start1) {
  int Tens; // make sure we capture the integer part of division by 10
  
  // Deal with home score
  // 100's - First digit (either "1" or invisible)
  SetScore100( Start100, ScoreColor, Score);
  
  // 10's - Second digit (if applicable)
  if(Score>99) { Score = Score-100; }
  
  if(Score>=10) {
    Tens = Score/10;
    SetDigit(Start10, ScoreColor, Tens);
  } else {
    Tens = 0;
    SetDigitDark(Start10);
  }
  
  // 1's
  Score = Score-(10*Tens);
  
  SetDigit(Start1, ScoreColor, Score);
}

 

Deze functie is dus gedacht om te gebruiken voor beide scores en wordt dan als volgt gebruikt:

void SetScore(int HomeScore, CRGB HomeScoreColor, int GuestScore, CRGB GuestScoreColor) {
  SetScoreNumber(HomeScore,  HomeScoreColor,  StartLEDScoreHome100,   StartLEDScoreHome10,   StartLEDScoreHome1);
  SetScoreNumber(GuestScore, GuestScoreColor, StartLEDScoreGuests100, StartLEDScoreGuests10, StartLEDScoreGuests1);
}

 

We kunnen nu de score als volgt aanroepen:

SetScore(12, CRGB::Blue, 2, CRGB::Blue);

 

In de volgende post probeer ik het e.e.a. bij elkaar te zetten.


   
BeantwoordenCiteren
 Hans
(@hans)
Famed Member Admin
Deelgenomen: 11 jaar geleden
Berichten: 2677
 

Alles bij elkaar geplakt om te testen - code compiled, maar ik kan het hier dus niet testen 😜 

#define FASTLED_INTERNAL    // just used to mute the Pragma messages when compiling
#include <FastLED.h>

#define LED_PIN     5
#define NUM_LEDS    50
#define BRIGHTNESS  64
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];

// Fail counters
#define FailHomeStartLED      10     // First LED for the Home fail counter
#define FailGuestsStartLED    20    // First LED for the Guests fail counter

#define FailHomeLeftToRight   true  // Home: true = from left to right, false = from right to left
#define FailGuestsLeftToRight true  // Guests: true = from left to right, false = from right to left

#define FailLEDsPerDot        2     // Number of LEDs in a fail dot
#define FailDotCount          5     // Number of fails/dots for Home or Guests

// Fail dot colors
CRGB FailColors[FailDotCount] = { CRGB(255,255,0), CRGB(255,200,0),  CRGB(255,150,0),  CRGB(255,100,0),  CRGB(255,0,0) };

// or use CRGB names;
// CRGB FailColors[FailDotCount] = { CRGB::Yellow, CRGB::Yellow, CRGB::Yellow, CRGB::Orange, CRGB::Red };

// Structure of a number in segments (see end of code for standard - the order here is different)
#define SegmentA 8
#define SegmentB 16
#define SegmentC 48
#define SegmentD 40
#define SegmentE 32
#define SegmentF 0
#define SegmentG 24
#define SegmentB 56

// What LED segments are needed for numbers?
//    Segments orientation: H V V H V V H                         
//           Segments used: A B C D E F G      For number:
byte SegmentNumbers[10][7] = { { 1,1,1,1,1,1,0 },   // 0
                               { 0,1,1,0,0,0,0 },   // 1
                               { 1,1,0,1,1,0,1 },   // 2
                               { 1,1,1,1,0,0,1 },   // 3
                               { 0,1,1,0,0,1,1 },   // 4
                               { 1,0,1,1,0,1,1 },   // 5
                               { 1,0,1,1,1,1,1 },   // 6
                               { 1,1,1,0,0,0,0 },   // 7
                               { 1,1,1,1,1,1,1 },   // 8
                               { 1,1,1,1,0,1,1 } }; // 9

// Horizontal/Vertical number of LEDs per segment
#define SegmentHSize 8
#define SegmentVSize 8 

// Total number of LEDs per number
#define TotalSegmentSize (3*SegmentHSize)+(4*SegmentVSize)

// Size of time colon - 2 dots, each 2 LEDS
#define TimeSeparatorSize 4   
      
// Assume wiring: 
// Start at time minutes -> time colon -> seconds -> Fouls Home -> Period -> Foul Guests -> Score Home -> Score Guests

// Number and colon start positions
#define StartLEDMinutes10        0
#define StartLEDMinutes1         TotalSegmentSize
#define StartLEDSecondsSeparator StartLEDMinutes1 + TotalSegmentSize
#define StartLEDSeconds10        StartLEDSecondsSeparator + TimeSeparatorSize
#define StartLEDSeconds1         StartLEDSeconds10 + TotalSegmentSize
                          
#define StartLEDPeriod           FailHomeStartLED + (FailDotCount*FailLEDsPerDot) 

#define StartLEDScoreHome100     FailGuestsStartLED + (FailDotCount*FailLEDsPerDot)
#define StartLEDScoreHome10      StartLEDScoreHome100 + (2*SegmentVSize)
#define StartLEDScoreHome1       StartLEDScoreHome10 + TotalSegmentSize

#define StartLEDScoreGuests100   StartLEDScoreHome1 + (FailDotCount*FailLEDsPerDot)
#define StartLEDScoreGuests10    StartLEDScoreGuests100 + (2*SegmentVSize)
#define StartLEDScoreGuests1     StartLEDScoreGuests10 + TotalSegmentSize                       



void setup() {
    FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
    FastLED.setBrightness(  BRIGHTNESS );      // Set overall brightness
    FastLED.clear(true);                       // clear LEDs and Show
}


void loop()
{
   SetFail(3,4); // zet de foutelling voor Home (3) en Guests (4)
   SetTime(12,43,CRGB::Red);
   SetPeriod(StartLEDPeriod, CRGB::Green, 2);
   SetScore(12, CRGB::Blue, 2, CRGB::Blue); 
}

void SetFail(int FailCountHome, int FailCountGuests) {
  int Direction;

  FailHomeLeftToRight ? Direction = 1 : Direction = -1;
  
  for(int FailCounter=0; FailCounter<FailCountHome; FailCounter++) {
    for(int LEDsInDot=0; LEDsInDot<FailLEDsPerDot; LEDsInDot++) {
      leds[FailHomeStartLED + Direction*((FailCounter*FailLEDsPerDot) + LEDsInDot)] = FailColors[FailCounter];
    }
  }
  
  FailGuestsLeftToRight ? Direction = 1 : Direction = -1;
  
  for(int FailCounter=0; FailCounter<FailCountGuests; FailCounter++) {
    for(int LEDsInDot=0; LEDsInDot<FailLEDsPerDot; LEDsInDot++) {
      leds[FailGuestsStartLED + Direction*((FailCounter*FailLEDsPerDot) + LEDsInDot)] = FailColors[FailCounter];
    }
  }

  FastLED.show(); 
}




void SetTime(int Minutes, int Seconds, CRGB TimeColor) {
  int Tens;
  
  SetTimeColon(TimeColor);
  
  Tens = Minutes/10;
  SetDigit(StartLEDMinutes10, TimeColor, Tens);
  SetDigit(StartLEDMinutes1,  TimeColor, Minutes - (Tens*10) );
  
  Tens = Seconds/10;
  SetDigit(StartLEDSeconds10, TimeColor, Tens);
  SetDigit(StartLEDSeconds1,  TimeColor, Seconds - (Tens*10) );
}

void SetTimeColon(CRGB ColonColor) {
  for(int i=StartLEDSecondsSeparator; i<StartLEDSecondsSeparator+TimeSeparatorSize; i++) {
    leds[i]=ColonColor;
  }
}

void SetPeriod(int StartLED, CRGB PeriodColor, int Period) {
  if(Period<0) { 
    SetDigit(StartLED, CRGB::Black, Period);
  } else {
    SetDigit(StartLED, PeriodColor, Period);
  }  
}

void SetScore(int HomeScore, CRGB HomeScoreColor, int GuestScore, CRGB GuestScoreColor) {
  SetScoreNumber(HomeScore,  HomeScoreColor,  StartLEDScoreHome100,   StartLEDScoreHome10,   StartLEDScoreHome1);
  SetScoreNumber(GuestScore, GuestScoreColor, StartLEDScoreGuests100, StartLEDScoreGuests10, StartLEDScoreGuests1);
}

void SetScoreNumber(int Score, CRGB ScoreColor, int Start100, int Start10, int Start1) {
  int Tens; // make sure we capture the integer part of division by 10
  
  // Deal with home score
  // 100's - First digit (either "1" or invisible)
  SetScore100( Start100, ScoreColor, Score);
  
  // 10's - Second digit (if applicable)
  if(Score>99) { Score = Score-100; }
  
  if(Score>=10) {
    Tens = Score/10;
    SetDigit(Start10, ScoreColor, Tens);
  } else {
    Tens = 0;
    SetDigitDark(Start10);
  }
  
  // 1's
  Score = Score-(10*Tens);
  
  SetDigit(Start1, ScoreColor, Score);
}

void SetScore100(int StartLED, CRGB NumberColor, int Score) {
  if(Score<100) { NumberColor = CRGB::Black; } // do not show the 100 digit is score less than 100
  
  for(int i=0; i<(2*SegmentVSize); i++) {
    leds[StartLED+i] = NumberColor;
  }
}

void SetDigit(int StartLED, CRGB DigitColor, int DigitValue) {
  SetDigitDark(StartLED); // set LED to dark, do not make this change visible yet (avoid flicker)
  
  if( SegmentNumbers[DigitValue][0]==1 ) { SetSegment(StartLED+SegmentA, SegmentHSize, DigitColor); } // A = horizontal
  if( SegmentNumbers[DigitValue][1]==1 ) { SetSegment(StartLED+SegmentB, SegmentVSize, DigitColor); } // B = vertical
  if( SegmentNumbers[DigitValue][2]==1 ) { SetSegment(StartLED+SegmentC, SegmentVSize, DigitColor); } // C = vertical
  if( SegmentNumbers[DigitValue][3]==1 ) { SetSegment(StartLED+SegmentD, SegmentHSize, DigitColor); } // D = horizontal
  if( SegmentNumbers[DigitValue][4]==1 ) { SetSegment(StartLED+SegmentE, SegmentVSize, DigitColor); } // E = vertical
  if( SegmentNumbers[DigitValue][5]==1 ) { SetSegment(StartLED+SegmentF, SegmentVSize, DigitColor); } // F = vertical
  if( SegmentNumbers[DigitValue][6]==1 ) { SetSegment(StartLED+SegmentG, SegmentHSize, DigitColor); } // G = horizontal
} 

void SetDigitDark(int StartLED) {
  for(int i=StartLED; i<( StartLED+(3*SegmentHSize)+(4*SegmentVSize) );i++) { // Number has 3 Horizontal and 4 vertical segments
    leds[StartLED+i] = CRGB::Black;
  } 
}

void SetSegment(int StartLED, int LEDCount, CRGB SegmentColor) {
  for(int i=0; i<LEDCount; i++) {
    leds[StartLED+i] =SegmentColor;
  }
}


/* Standard: ABCDEFGH

      00 A
  40        08 
   F        B
      42 G       
  32        16
   E        C
      24 D       
   

#define SegmentA 0
#define SegmentB 8
#define SegmentC 16
#define SegmentD 24
#define SegmentE 32
#define SegmentF 40
#define SegmentG 48
#define SegmentB 56 */

   
BeantwoordenCiteren
Pagina 3 / 6
Deel: