Arduino String samenvoegen handleiding: uitgebreide gids voor werken met strings, arrays en de String-klasse

Dit artikel behandelt hoe je in Arduino met tekst werkt: van eenvoudige arrays van chars tot de krachtige String-objecten. We leggen uit wat een string is, hoe arrays werken, welke valkuilen er zijn bij toewijzing en concatenatie, en welke methoden en concepten handig zijn bij het bouwen van robuuste toepassingen. We nemen letterlijk zinnen uit het draft om een zo compleet mogelijk beeld te schetsen, en zetten waar nodig extra uitleg neer om de samenhang te waarborgen.

Overzicht van werken met tekst: Strings en hun fundament

Elke keer als we “Serial.print()” of “Serial.println()” gebruiken, sturen we een string. We kunnen als eerste beginnen met het zogenaamde “string” (allemaal kleine letters!) datatype - hierbij is een string een zogenaamde “array” van karakters (char), of te wel een “array of char”. Een “array” (Array Data Type, Array Data Structuur) kan gezien worden als een verzameling elementen welke allemaal van hetzelfde data type zijn. Hierbij kan ieder element van de “lijst” (array) met een index nummer benaderd worden.

In het geval van een string (allemaal kleine letters!) is dat ook het geval. De variabele die we als array gaan definiëren wijst ook weer naar de geheugen locatie waar de waarden zijn opgeslagen. In het geval van een array echter, omdat er meerder waarden kunnen zijn, wijst het naar het eerste element in de lijst. Ja, een NULL karakter, of te wel een “niks”. Dit is een speciaal karakter, en het eerste karakter, in de lijst van karakters die de Arduino kent, en heeft eigenlijk geen waarde - het kan niet worden weergegeven.

Arraylengte en geheugenbewustzijn

De vierkante haken geven vervolgens aan de compiler door dat de compiler maar moet bepalen hoeveel elementen er in de array gaan zitten. We kunnen tussen de vierkante haken ook een nummer zetten, wat dan aangeeft hoeveel elementen er in de array moeten gaan zitten - maar dan moet je dus wel in de gaten houden dat de lengte van de array gelijk moet zijn aan het aantal letters in de tekst PLUS het NULL karakter aan het einde. In het voorbeeld heeft de array dus 5 elementen: De letters “H”, “a”, “n” en “s” en het NULL karakter.

Het is goed om te weten dat als je een groter nummer gebruikt dan wat nodig is, dat dit geen kwaad kan. Het neemt alleen wat meer geheugen ruimte in beslag, wat handig kan zijn als de tekst groter wordt als het programma draait. Als we de hele variabele “Naam” naar “print” sturen, dan wordt het begin adres doorgegeven, maar omdat we geen specifieke positie hebben opgegeven, zal het programma alle karakters weer gaan geven tot het NULL karakter wordt gevonden.

Waarom strings soms lastig zijn

We zijn al bekend met variabelen, en een beetje bekend met strings (tekst) definitie en initiële inhoud. De Arduino compiler geeft dan een foutmelding “invalid array assignment”. Of te wel “verkeerde array toewijzing”. Nou eigenlijk niet. Kun je jezelf nog herinneren dat we zeiden dat een variabele naar de geheugen locatie van de waarde wijst? Dus bij een array, naar het eerste element van de “lijst”? Het is dus een geheugen locatie van een char (karakter) en dus niet een string. Geloof me, dit probleem zul je nog wel een paar keer tegen komen, en het is één van de redenen waarom ik geen fan ben van de programmeertaal C. Ik ben meer een Pascal-fan - en er zullen legio mensen zijn die het daar niet mee eens zijn.

Speciale tekens en escape-methoden

Er zijn een aantal van dit soort speciale tekens, bijvoorbeeld de dubbele haakjes die we gebruiken om rond een string tekst te zetten. De eerste regel is onze foute regel. Je ziet dat trefwoorden die van belang zijn oranje worden weergegeven, en dat een string een beetje groen-achtig is. De derde regel laat ons de zogenaamde “escape” methode zien. Deze truc is ook nodig voor een bepaald aantal andere speciale teken (kijk in de ASCII HTML tabel, in de “Esc” kolom).

Karakter voor karakter manipuleren en stringlengte

De foutmelding (in het Engels: error of error message) is: invalid conversion from 'const char*' to 'char' wat ons zegt dat we een verkeerd datatype proberen toe te wijzen aan een van onze array-elementen. In andere woorden: we proberen een string toe te wijzen aan karakter (char). Om dit toch te kunnen doen, moeten we dus een char gebruiken in plaats van een string. Een enkele char (karakter) geven we daarom aan met enkele aanhalingstekens ( ‘ ).

Hoewel “Hans” en “Bram” alle twee even lang zijn, kunnen we Naam[4] overslaan. Zie je hoe we Naam[3] een nummer toewijzen? Dit kan omdat we een nummer kunnen toewijzen zolang dat nummer tussen 0 en 256 blijft (een byte) omdat dit overeenkomt met de lengte van een char.

Vergeet dus niet dat een char ook enumert en dus geteld kan worden. “strlen()” (lees als: string Length - of te wel: string lengte) heeft maar 1 parameter nodig, de string (array) en geeft terug hoe veel tekens er in een string staan. “sizeof()” doet hetzelfde als “strlen()”, maar deze neemt WEL de NULL karakter mee in de telling.

Concatenatie en het samenvoegen van strings

“strcat()” is een vreemde eend, het plakt twee array’s (strings) aan elkaar. Dus we zeggen nu dat de compiler maar moet uitzoeken hoe groot de array moet worden, en de compiler baseert dat op de initiële waarde toewijzing. Dat komt omdat de array maar 5 tekens lang is en we duidelijk voorbij “5” gaan als we het stukje ” heeft 2 neefjes, genaamd Bram en Max!” en “Naam” aan elkaar gaan plakken. De Arduino gaat lekker onbenullig voorbij de 5, en dus kijken naar geheugen dat helemaal niet meer bij deze array hoort, en dus “rommel” oplevert. Niet iets wat we willen hè? En dat is precies de reden waarom we op moeten letten als we de lengte van een array definiëren.

Overgang naar het String-object

We hebben net gekeken naar de “string” als een Array of Char. De onhandige dingen die we deden met de oude “string” (kleine letter “s” dus een Array van Char), zijn met het “String” (hoofdletter “S”, dus het object) object veel eenvoudiger. De korte uitleg: een “object” is een “ding” of “item” met eigenschappen (properties) en methoden (methods). Zo een “ding” kan van alles zijn, b.v. Om het eenvoudig te houden, zouden we kunnen zeggen dat de auto een aantal eigenschappen (Engels: properties!) heeft zoals: kleur, werkende moter, wat benzine, aantal deuren, een achterbak, en wielen. Dit noemen we dus “properties” (eigenschappen). Een property kan een waarde hebben of de waarde kan ontbreken. Zo kan de auto een “bestuurder” hebben, of geen “bestuurder” hebben. In dat laatste geval is “bestuurder” gelijk aan NULL.

Voordelen van de String-klasse

Een andere reden, welke misschien belangrijker is, is dat een object zich als een soort datatype kan gedragen, waarbij elke variabele van dat type zich op precies dezelfde manier gedraagt en precies dezelfde soort properties heeft. We kunnen dus een variabele “MijnAuto” van het object type “auto” definiëren. Kleine moeite en ineens zijn alle methoden en properties beschikbaar voor “MijnAuto”.

We hebben met “Serial” al een aantal methods (functies of methoden) zoals “begin”, “print” en “println”. We gebruiken deze methoden om ons object (Serial) iets te laten doen. Zie je nu dat we een dergelijk functie altijd aanroepen in dit formaat: OBJECT.METHODE of OBJECT.PROPERTY ….?

De String-klasse biedt een eenvoudigere manier om strings te beheren. Net als met “string” kunnen we “String” ook gewoon gebruiken als we “Serial.print()” aanroepen. Regel 10 kan overigens ook geschreven worden als Naam = String("Bram");, wat dus ook wert. Het verschil is echter dat we in deze tweede methode een nieuwe “String” object maken met de tekst “Bram”, terwijl we in de eerste methode alleen maar de tekst “Bram” doorgeven.

Lange teksten en geheugenbeheer

De tweede methode wordt vaak gebruikt om b.v. lange tekst te bouwen zonder zorgen over de arraylengte zoals bij de “string”. Het String-object neemt echter meer geheugen in beslag - je kunt het als een aanvulling zien. Hieronder een voorbeeld van enkele methoden, kijk ook eens bij de Arduino String Object Reference. In regel 12, bepalen we de lengte van de String tekst, en dat is in dit geval zonder het NULL karakter mee te tellen. Hiervoor gebruiken we de “length()” methode van het “String” object: Naam.length(). In regel 14, gebruiken we de methode “concat()” (Engels voor “samenvoegen”) om de tekst ” heeft 2 neefjes: Bram en Max!” aan de String “Hans” te plakken. Zoals je ziet werkt dat prima. Maar … er is zelfs een eenvoudigere methode om “String”’s aan elkaar te plakken. Gewoon door het gebruik van het plus (+) teken. We kunnen dit zelfs doen met de compound operators die we eerder hebben gezien.

String en printen

Het “String” object laat nog meer van z’n goede kant zien als we een nummer willen omzetten en in een String willen zetten. Nu we weten dat String("some text") een “String” object terug geeft, en dat we eenvoudig tekst aan elkaar kunnen plakken met het plus (+) teken, kan ik je ook laten zien hoe we de “Serial.println()” stukken eenvoudig kunnen gebruiken en vele zaken die we voorheen in meerdere regels moesten doen, ineens in een enkele regel kan.

Advanced: vergelijken, verwerken en verwerken van inkomende data

Strings vergelijken is mogelijk maar heeft zijn eigen eigenaardigheden. De reden waarom dit fout kan lopen heeft te maken met het feit dat een variabele naar een geheugenlocatie wijst - een pointer. Wanneer je strings vergelijkt, kijkt de functie naar ASCII-waarden van individuele karakters. Het kan leiden tot onverwachte uitkomsten zoals “Hoi” groter dan “Hans” terwijl menselijk gezien andersom verwacht zou worden. Ook bij het vergelijken van String-objecten kunnen afwijkingen voorkomen, gelijkaardig aan “strcmp()” in C.

  1. Incoming string data verwerken: vaak wordt Arduino gebruikt om inkomende stringdata te verwerken via seriële communicatie, waarbij men keuzes maakt tussen verschillende strategieën zoals directe concatenatie of een state-machine.
  2. Geheugenbeperkingen: RAM en program memory (PROGMEM) zijn schaars; benaderingen die geheugen besparen zijn waardevol.
  3. Concateneren en delimiters: leeskarakter-voor-karakter verwerken tot een delimiter (bijv. newline) wordt vaak toegepast.
  4. Stellingen over STL-string: Arduino ondersteunt de STL-stringklasse (lower-case 'string') naast de eigen String-klasse; de STL-variant werkt anders met geheugenfragmentatie.
  5. Reserve-functie en geheugenfragmentatie: gebruik van reserve kan fragmentatie verminderen bij de STL-string.

Praktische toepassingen: bridges en voorbeelden

De String-klasse is onderdeel van de Arduino IDE; er hoeven geen extra include-directives te worden toegevoegd. Voorbeelden in projecten tonen hoe string- en serial-communicatie kan worden opgebouwd en gebruikt in allerlei scenario's, zoals communicatie met Java-applicaties via seriële verbindingen, of het aansturen van LED’s en hardware op basis van stringcommando’s.

Infographic: Verschillende stringtypes en wanneer ze te gebruiken (char-array, String-object, STL-string)

In complexe projecten kan men kiezen tussen “String” objecten voor gemak en de C-stijl strings voor geheugenoptimalisatie. Een goede aanpak is om te bepalen of snelheid, geheugen of leesbaarheid de prioriteit heeft en daarop te kiezen tussen de varianten, bijvoorbeeld voor een stoplichtproject waarbij poorten PWM-genot en serialisatie vereist is.

Praktische stappen: voorbeeldscenario met stoplicht en serial communicatie

In een typisch Arduino-project met een stoplicht en een Java-applicatie via seriële poort, kan de volgende opzet gelden: een sketch met setup() en loop(), gebruik van Serial.begin(), en het verzenden van statusupdates via Serial.println(). De communicatie kan checks en commando’s op de seriële lijn afhandelen.

Schema van seriële communicatie tussen Java-app en Arduino

Escape-tekens en voorbeeldcode

In het voorbeeld wordt beschreven hoe escape-methoden nodig zijn voor speciale tekens en hoe concatenatie kan worden uitgevoerd met zowel de plus-operator als concat() voor de String-object. De uitleg laat zien hoe het combineren van strings via verschillende methoden kan leiden tot leesbare en compacte code, zonder de stabiliteit van het programma in gevaar te brengen.

Technische basis: korte samenvatting van relevante functies

  • strlen() - geeft de lengte van een string (zonder NULL bij char-array).
  • sizeof() - geeft de totale grootte van de array, inclusief NULL terminator.
  • strcat() - voegt strings aan elkaar (concateneren).
  • String.length() - lengte van een String-object.
  • String.concat() - voeg een string aan een andere string toe.
  • String-operator + - eenvoudige concatenatie van strings.
  • Serial.print()/Serial.println() - verzenden van strings naar de seriële poort.

Strings in Arduino IDE: How to use String data structure

Toepassingsvoorbeelden en verdere daaruit voortvloeiende concepten

De combinatie van strings met getallen via interne conversie maakt het mogelijk om in één regel meerdere taken te voltooien, zoals het printen van gecombineerde boodschappen of het tonen van dynamische status op een console. Het gebruik van String-objecten kan de leesbaarheid vergroten, terwijl C-stijl strings de maximum mogelijke geheugenbesparingen geven.

tags: #arduino #tekst #samen #voegen