diff --git a/ros2rag.base/src/main/jastadd/Analysis.jrag b/ros2rag.base/src/main/jastadd/Analysis.jrag
index 246645f2e92f18ef325466530f95e5b5f37847a5..6c8755e77db354ed36900f3439cd6fd8c5e6f2f5 100644
--- a/ros2rag.base/src/main/jastadd/Analysis.jrag
+++ b/ros2rag.base/src/main/jastadd/Analysis.jrag
@@ -4,8 +4,9 @@ aspect Analysis {
   syn boolean JavaTypeUse.isPrimitiveType() = false;
   eq SimpleJavaTypeUse.isPrimitiveType() {
     switch(getName()) {
-      case "short":
       case "int":
+      case "short":
+      case "long":
       case "float":
       case "double":
       case "char":
diff --git a/ros2rag.base/src/main/jastadd/Navigation.jrag b/ros2rag.base/src/main/jastadd/Navigation.jrag
index a43beba54597e06cae95d577e610b5bba5b1e2c9..ce2029496eebf41a537d8f72554e8ddb2021a675 100644
--- a/ros2rag.base/src/main/jastadd/Navigation.jrag
+++ b/ros2rag.base/src/main/jastadd/Navigation.jrag
@@ -34,4 +34,8 @@ aspect Navigation {
     }
     return null;
   }
+
+  // --- isDefaultMappingDefinition ---
+  syn boolean MappingDefinition.isDefaultMappingDefinition() = false;
+  eq DefaultMappingDefinition.isDefaultMappingDefinition() = true;
 }
diff --git a/ros2rag.base/src/main/jastadd/backend/Generation.jadd b/ros2rag.base/src/main/jastadd/backend/Generation.jadd
index b74743d183fd56390537348e3be580090b77e3f3..e827c68c947baf50b43bf8abfc2651dfc7bced2c 100644
--- a/ros2rag.base/src/main/jastadd/backend/Generation.jadd
+++ b/ros2rag.base/src/main/jastadd/backend/Generation.jadd
@@ -95,7 +95,7 @@ aspect AspectGeneration {
     for (UpdateDefinition def : getUpdateDefinitionList()) {
       def.generateAspect(sb);
     }
-    for (MappingDefinition def : getMappingDefinitionList()) {
+    for (MappingDefinition def : allMappingDefinitions()) {
       def.generateAspect(sb);
     }
     for (DependencyDefinition def : getDependencyDefinitionList()) {
@@ -112,11 +112,11 @@ aspect AspectGeneration {
     final String resultVariablePrefix = "result";  // we do not need "_" here, because methodName begins with one
     String inputVariableName = initialInputVariableName;
     // last variable need to be declared before begin of try
-    MappingDefinition lastDefinition = getMappingList().get(getMappingList().size() - 1);
+    MappingDefinition lastDefinition = effectiveMappings().get(effectiveMappings().size() - 1);
     sb.append(ind(indent)).append(lastDefinition.getToType().prettyPrint()).append(" ")
       .append(resultVariablePrefix).append(lastDefinition.methodName()).append(";\n");
     sb.append(ind(indent)).append("try {\n");
-    for (MappingDefinition mappingDefinition : getMappingList()) {
+    for (MappingDefinition mappingDefinition : effectiveMappings()) {
       String resultVariableName = resultVariablePrefix + mappingDefinition.methodName();
       sb.append(ind(indent + 1));
       if (mappingDefinition != lastDefinition) {
@@ -135,6 +135,9 @@ aspect AspectGeneration {
       sb.append(ind(indent)).append("if (").append(preemptiveExpectedValue());
       if (getToken().isPrimitiveType()) {
         sb.append(" == ").append(inputVariableName);
+      } else if (effectiveMappings().get(effectiveMappings().size() - 1).isDefaultMappingDefinition()) {
+        sb.append(" != null && ").append(preemptiveExpectedValue()).append(".equals(")
+          .append(inputVariableName).append(")");
       } else {
         sb.append(" != null ? ").append(preemptiveExpectedValue()).append(".equals(")
           .append(inputVariableName).append(")").append(" : ")
diff --git a/ros2rag.base/src/main/jastadd/backend/Mappings.jrag b/ros2rag.base/src/main/jastadd/backend/Mappings.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..4bfe070a81b49e63996ba8f9a32a43eb13af6d1f
--- /dev/null
+++ b/ros2rag.base/src/main/jastadd/backend/Mappings.jrag
@@ -0,0 +1,204 @@
+aspect DefaultMappings {
+
+  private DefaultMappingDefinition Ros2Rag.baseDefaultMappingDefinitionFromBytes(String typeName) {
+    DefaultMappingDefinition result = new DefaultMappingDefinition();
+    result.setID("_DefaultBytesTo" + Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1) + "Mapping");
+    result.setFromType(new JavaArrayMappingDefinitionType(new SimpleJavaTypeUse("byte")));
+    result.setFromVariableName("bytes");
+    result.setToType(new JavaMappingDefinitionType(new SimpleJavaTypeUse(typeName)));
+    return result;
+  }
+
+  private DefaultMappingDefinition Ros2Rag.baseDefaultMappingDefinitionToBytes(String typeName) {
+    DefaultMappingDefinition result = new DefaultMappingDefinition();
+    result.setID("_Default" + Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1) + "ToBytesMapping");
+    result.setFromType(new JavaMappingDefinitionType(new SimpleJavaTypeUse(typeName)));
+    result.setFromVariableName("input");
+    result.setToType(new JavaArrayMappingDefinitionType(new SimpleJavaTypeUse("byte")));
+    return result;
+  }
+
+  syn nta DefaultMappingDefinition Ros2Rag.defaultBytesToIntMapping() {
+    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("int");
+    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getInt();");
+    return result;
+  }
+
+  syn nta DefaultMappingDefinition Ros2Rag.defaultBytesToShortMapping() {
+    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("short");
+    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getShort();");
+    return result;
+  }
+
+  syn nta DefaultMappingDefinition Ros2Rag.defaultBytesToLongMapping() {
+    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("long");
+    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getLong();");
+    return result;
+  }
+
+  syn nta DefaultMappingDefinition Ros2Rag.defaultBytesToFloatMapping() {
+    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("float");
+    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getFloat();");
+    return result;
+  }
+
+  syn nta DefaultMappingDefinition Ros2Rag.defaultBytesToDoubleMapping() {
+    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("double");
+    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getDouble();");
+    return result;
+  }
+
+  syn nta DefaultMappingDefinition Ros2Rag.defaultBytesToCharMapping() {
+    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("char");
+    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getChar();");
+    return result;
+  }
+
+  syn nta DefaultMappingDefinition Ros2Rag.defaultBytesToStringMapping() {
+    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("String");
+    result.setContent("return new String(bytes);");
+    return result;
+  }
+
+  syn nta DefaultMappingDefinition Ros2Rag.defaultIntToBytesMapping() {
+    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("int");
+    result.setContent("return java.nio.ByteBuffer.allocate(4).putInt(input).array();");
+    return result;
+  }
+
+  syn nta DefaultMappingDefinition Ros2Rag.defaultShortToBytesMapping() {
+    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("short");
+    result.setContent("return java.nio.ByteBuffer.allocate(2).putShort(input).array();");
+    return result;
+  }
+
+  syn nta DefaultMappingDefinition Ros2Rag.defaultLongToBytesMapping() {
+    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("long");
+    result.setContent("return java.nio.ByteBuffer.allocate(8).putLong(input).array();");
+    return result;
+  }
+
+  syn nta DefaultMappingDefinition Ros2Rag.defaultFloatToBytesMapping() {
+    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("float");
+    result.setContent("return java.nio.ByteBuffer.allocate(4).putFloat(input).array();");
+    return result;
+  }
+
+  syn nta DefaultMappingDefinition Ros2Rag.defaultDoubleToBytesMapping() {
+    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("double");
+    result.setContent("return java.nio.ByteBuffer.allocate(8).putDouble(input).array();");
+    return result;
+  }
+
+  syn nta DefaultMappingDefinition Ros2Rag.defaultCharToBytesMapping() {
+    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("char");
+    result.setContent("return java.nio.ByteBuffer.allocate(2).putChar(input).array();");
+    return result;
+  }
+
+  syn nta DefaultMappingDefinition Ros2Rag.defaultStringToBytesMapping() {
+    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("String");
+    result.setContent("return input.getBytes();");
+    return result;
+  }
+}
+
+aspect Mappings {
+  // --- effectiveMappings ---
+  syn java.util.List<MappingDefinition> UpdateDefinition.effectiveMappings();
+  eq ReadFromMqttDefinition.effectiveMappings() {
+    // if there is a first mapping, check if its input type is byte[].
+    //  or if no mappings are specified.
+    // then prepend the suitable default mapping
+    java.util.List<MappingDefinition> result;
+    if (getMappingList().size() == 0 || !getMappingList().get(0).getFromType().isByteArray()) {
+      result = new java.util.ArrayList();
+      result.add(suitableDefaultMapping());
+      result.addAll(getMappingList());
+    } else {
+      result = getMappingList();
+    }
+    return result;
+  }
+  eq WriteToMqttDefinition.effectiveMappings() {
+    // if there is a mapping, check if the output type of the last mapping is byte[].
+    //  or if no mappings are specified.
+    // then append the suitable default mapping
+    java.util.List<MappingDefinition> result;
+    int numMappings = getMappingList().size();
+    if (numMappings == 0 || !getMappingList().get(numMappings - 1).getToType().isByteArray()) {
+      result = new java.util.ArrayList();
+      result.addAll(getMappingList());
+      result.add(suitableDefaultMapping());
+    } else {
+      result = getMappingList();
+    }
+    return result;
+  }
+
+  // --- suitableDefaultMapping ---
+  syn DefaultMappingDefinition UpdateDefinition.suitableDefaultMapping();
+  eq ReadFromMqttDefinition.suitableDefaultMapping() {
+    String typeName = getToken().getJavaTypeUse().getName();
+    switch(typeName) {
+      case "int":
+      case "Integer": return ros2rag().defaultBytesToIntMapping();
+      case "short":
+      case "Short": return ros2rag().defaultBytesToShortMapping();
+      case "long":
+      case "Long": return ros2rag().defaultBytesToLongMapping();
+      case "float":
+      case "Float": return ros2rag().defaultBytesToFloatMapping();
+      case "double":
+      case "Double": return ros2rag().defaultBytesToDoubleMapping();
+      case "char":
+      case "Character": return ros2rag().defaultBytesToCharMapping();
+      case "String": return ros2rag().defaultBytesToStringMapping();
+      default: return null;
+    }
+  }
+  eq WriteToMqttDefinition.suitableDefaultMapping() {
+    String typeName = getToken().getJavaTypeUse().getName();
+    switch(typeName) {
+      case "int":
+      case "Integer": return ros2rag().defaultIntToBytesMapping();
+      case "short":
+      case "Short": return ros2rag().defaultShortToBytesMapping();
+      case "long":
+      case "Long": return ros2rag().defaultLongToBytesMapping();
+      case "float":
+      case "Float": return ros2rag().defaultFloatToBytesMapping();
+      case "double":
+      case "Double": return ros2rag().defaultDoubleToBytesMapping();
+      case "char":
+      case "Character": return ros2rag().defaultCharToBytesMapping();
+      case "String": return ros2rag().defaultStringToBytesMapping();
+      default: return null;
+    }
+  }
+
+  // --- isByteArray ---
+  syn boolean MappingDefinitionType.isByteArray() = false;
+  eq JavaArrayMappingDefinitionType.isByteArray() = getType().getName().equals("byte");
+
+  // --- allMappingDefinitions ---
+  syn java.util.List<MappingDefinition> Ros2Rag.allMappingDefinitions() {
+    java.util.List<MappingDefinition> result = new java.util.ArrayList<>();
+    getMappingDefinitionList().iterator().forEachRemaining(result::add);
+    result.add(defaultBytesToIntMapping());
+    result.add(defaultBytesToShortMapping());
+    result.add(defaultBytesToLongMapping());
+    result.add(defaultBytesToFloatMapping());
+    result.add(defaultBytesToDoubleMapping());
+    result.add(defaultBytesToCharMapping());
+    result.add(defaultBytesToStringMapping());
+    result.add(defaultIntToBytesMapping());
+    result.add(defaultShortToBytesMapping());
+    result.add(defaultLongToBytesMapping());
+    result.add(defaultFloatToBytesMapping());
+    result.add(defaultDoubleToBytesMapping());
+    result.add(defaultCharToBytesMapping());
+    result.add(defaultStringToBytesMapping());
+    return result;
+  }
+}
diff --git a/ros2rag.base/src/main/jastadd/parser/Ros2Rag.parser b/ros2rag.base/src/main/jastadd/parser/Ros2Rag.parser
index f2ab264c5b39a53d2dd37634b30b0d11c899dce1..015b4e664950fd9f49601dbea53fc67225c41169 100644
--- a/ros2rag.base/src/main/jastadd/parser/Ros2Rag.parser
+++ b/ros2rag.base/src/main/jastadd/parser/Ros2Rag.parser
@@ -12,8 +12,6 @@ Ros2Rag ros2rag
   }
 :} ;
 
-// read Joint.CurrentPosition using LinkStateToIntPosition ;
-// write RobotArm._AppropriateSpeed using CreateSpeedMessage ;
 UpdateDefinition update_definition
   = READ ID.type_name DOT ID.token_name SCOL
     {:
@@ -52,7 +50,6 @@ ArrayList string_list
   | string_list COMMA ID
 ;
 
-// RobotArm._AppropriateSpeed canDependOn Joint.CurrentPosition as dependency1 ;
 DependencyDefinition dependency_definition
   = ID.target_type DOT ID.target_token CAN_DEPEND_ON ID.source_type DOT ID.source_token AS ID.id SCOL
     {:
@@ -64,10 +61,6 @@ DependencyDefinition dependency_definition
     :}
 ;
 
-//LinkStateToIntPosition maps protobuf panda.Linkstate.PandaLinkState x to IntPosition y {
-//  panda.Linkstate.PandaLinkState.Position p = x.getPos();
-//  y = IntPosition.of((int) p.getPositionX(), (int) p.getPositionY(), (int) p.getPositionZ());
-//}
 MappingDefinition mapping_definition
   = ID.id MAPS mapping_type.from_type ID.from_name TO mapping_type.to_type MAPPING_CONTENT.content
     {:
diff --git a/ros2rag.tests/build.gradle b/ros2rag.tests/build.gradle
index c37cfdd23f9a7ffd87393fda2e722c821983eff7..6c939f991b113912ea1c777a909d56794c8459e2 100644
--- a/ros2rag.tests/build.gradle
+++ b/ros2rag.tests/build.gradle
@@ -54,6 +54,7 @@ protobuf {
     }
 }
 
+// --- Test: Example ---
 task preprocessExampleTest(type: JavaExec, group: 'verification') {
     doFirst {
         delete 'src/test/02-after-ros2rag/example/Grammar.relast',
@@ -70,22 +71,6 @@ task preprocessExampleTest(type: JavaExec, group: 'verification') {
             '--rootNode=Model'
 }
 
-//task compileExampleTest(type: JavaExec, group: 'verification') {
-//
-//    doFirst {
-//        delete 'src/test/java-gen/example'
-//    }
-//
-//    classpath = sourceSets.main.runtimeClasspath
-//    main = 'org.jastadd.JastAdd'
-//    //noinspection GroovyAssignabilityCheck
-//    args '--o=src/test/java-gen/', '--package=example.ast',
-//            'src/test/jastadd-gen/example/Grammar.relast',
-//            'src/test/jastadd-gen/example/MqttUpdater.java',
-//            'src/test/jastadd-gen/example/ROS2RAG.jadd',
-//            'src/test/jastadd/Example.jadd'
-//}
-
 task compileExampleTest(type: RelastTest) {
     verbose = true
     useJastAddNames = true
@@ -101,6 +86,38 @@ task compileExampleTest(type: RelastTest) {
 test.dependsOn compileExampleTest
 compileExampleTest.dependsOn preprocessExampleTest
 
+// --- Test: default-only-read ---
+task preprocessDefaultOnlyReadTest(type: JavaExec, group: 'verification') {
+    doFirst {
+        delete 'src/test/02-after-ros2rag/defaultOnlyRead/Grammar.relast',
+                'src/test/02-after-ros2rag/defaultOnlyRead/MqttUpdater.java',
+                'src/test/02-after-ros2rag/defaultOnlyRead/ROS2RAG.jadd'
+    }
+
+    classpath = sourceSets.main.runtimeClasspath
+    main = 'org.jastadd.ros2rag.compiler.Compiler'
+    //noinspection GroovyAssignabilityCheck
+    args '--outputDir=src/test/02-after-ros2rag/defaultOnlyRead',
+            '--inputGrammar=src/test/01-input/defaultOnlyRead/Example.relast',
+            '--inputRos2Rag=src/test/01-input/defaultOnlyRead/Example.ros2rag',
+            '--rootNode=A',
+            '--verbose'
+}
+
+task compileDefaultOnlyReadTest(type: RelastTest) {
+    verbose = true
+    useJastAddNames = true
+    jastAddList = 'JastAddList'
+    relastFiles 'src/test/02-after-ros2rag/defaultOnlyRead/Grammar.relast'
+    grammarName = 'src/test/03-after-relast/defaultOnlyRead/defaultOnlyRead'
+    packageName = 'defaultOnlyRead.ast'
+    moreInputFiles 'src/test/02-after-ros2rag/defaultOnlyRead/MqttUpdater.jadd',
+            'src/test/02-after-ros2rag/defaultOnlyRead/ROS2RAG.jadd'
+}
+
+test.dependsOn compileDefaultOnlyReadTest
+compileDefaultOnlyReadTest.dependsOn preprocessDefaultOnlyReadTest
+
 clean {
-    delete 'src/test/02-after-ros2rag/example/', 'src/test/03-after-relast/example/'
+    delete 'src/test/02-after-ros2rag/*/', 'src/test/03-after-relast/*/'
 }
diff --git a/ros2rag.tests/src/test/01-input/defaultOnlyRead/Example.relast b/ros2rag.tests/src/test/01-input/defaultOnlyRead/Example.relast
new file mode 100644
index 0000000000000000000000000000000000000000..30eba76b346dace40c72ca4e172b88ef206ea063
--- /dev/null
+++ b/ros2rag.tests/src/test/01-input/defaultOnlyRead/Example.relast
@@ -0,0 +1,3 @@
+A ::= NativeTypes* BoxedTypes* ;
+NativeTypes ::= <IntValue:int> <ShortValue:short> <LongValue:long> <FloatValue:float> <DoubleValue:double> <CharValue:char> <StringValue:String> ;
+BoxedTypes ::= <IntValue:Integer> <ShortValue:Short> <LongValue:Long> <FloatValue:Float> <DoubleValue:Double> <CharValue:Character> ;
diff --git a/ros2rag.tests/src/test/01-input/defaultOnlyRead/Example.ros2rag b/ros2rag.tests/src/test/01-input/defaultOnlyRead/Example.ros2rag
new file mode 100644
index 0000000000000000000000000000000000000000..90e2ccabb89b8d1f48ad1ba01fa9112e5c762a70
--- /dev/null
+++ b/ros2rag.tests/src/test/01-input/defaultOnlyRead/Example.ros2rag
@@ -0,0 +1,15 @@
+// --- update definitions ---
+read NativeTypes.IntValue;
+read NativeTypes.ShortValue;
+read NativeTypes.LongValue;
+read NativeTypes.FloatValue;
+read NativeTypes.DoubleValue;
+read NativeTypes.CharValue;
+read NativeTypes.StringValue;
+
+read BoxedTypes.IntValue;
+read BoxedTypes.ShortValue;
+read BoxedTypes.LongValue;
+read BoxedTypes.FloatValue;
+read BoxedTypes.DoubleValue;
+read BoxedTypes.CharValue;
diff --git a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyReadTest.java b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyReadTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..88b51d69cdef68399297768ebdd98c6631508b8b
--- /dev/null
+++ b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyReadTest.java
@@ -0,0 +1,139 @@
+package org.jastadd.ros2rag.tests;
+
+import defaultOnlyRead.ast.A;
+import defaultOnlyRead.ast.BoxedTypes;
+import defaultOnlyRead.ast.MqttUpdater;
+import defaultOnlyRead.ast.NativeTypes;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test case "defaultOnlyRead".
+ *
+ * @author rschoene - Initial contribution
+ */
+public class DefaultOnlyReadTest {
+
+  private static final String TOPIC_NATIVE_INT = "native/int";
+  private static final String TOPIC_NATIVE_SHORT = "native/short";
+  private static final String TOPIC_NATIVE_LONG = "native/long";
+  private static final String TOPIC_NATIVE_FLOAT = "native/float";
+  private static final String TOPIC_NATIVE_DOUBLE = "native/double";
+  private static final String TOPIC_NATIVE_CHAR = "native/char";
+  private static final String TOPIC_NATIVE_STRING = "native/string";
+
+  private static final String TOPIC_BOXED_INTEGER = "boxed/Integer";
+  private static final String TOPIC_BOXED_SHORT = "boxed/Short";
+  private static final String TOPIC_BOXED_LONG = "boxed/Long";
+  private static final String TOPIC_BOXED_FLOAT = "boxed/Float";
+  private static final String TOPIC_BOXED_DOUBLE = "boxed/Double";
+  private static final String TOPIC_BOXED_CHARACTER = "boxed/Character";
+
+  private A model;
+  private NativeTypes integers;
+  private NativeTypes floats;
+  private NativeTypes chars;
+  private BoxedTypes allBoxed;
+  private MqttUpdater sender;
+
+  @AfterEach
+  public void closeConnections() {
+    if (sender != null) {
+      sender.close();
+    }
+    if (model != null) {
+      model.MqttCloseConnections();
+    }
+  }
+
+
+  @Test
+  public void buildModel() {
+    createModel();
+  }
+
+  @Test
+  public void communicate() throws IOException, InterruptedException {
+    createModel();
+
+    model.MqttSetHost(TestUtils.getMqttHost());
+    assertTrue(model.MqttWaitUntilReady(2, TimeUnit.SECONDS));
+
+    integers.connectIntValue(TOPIC_NATIVE_INT);
+    integers.connectShortValue(TOPIC_NATIVE_SHORT);
+    integers.connectLongValue(TOPIC_NATIVE_LONG);
+    floats.connectFloatValue(TOPIC_NATIVE_FLOAT);
+    floats.connectDoubleValue(TOPIC_NATIVE_DOUBLE);
+    chars.connectCharValue(TOPIC_NATIVE_CHAR);
+    chars.connectStringValue(TOPIC_NATIVE_STRING);
+    allBoxed.connectIntValue(TOPIC_BOXED_INTEGER);
+    allBoxed.connectShortValue(TOPIC_BOXED_SHORT);
+    allBoxed.connectLongValue(TOPIC_BOXED_LONG);
+    allBoxed.connectFloatValue(TOPIC_BOXED_FLOAT);
+    allBoxed.connectDoubleValue(TOPIC_BOXED_DOUBLE);
+    allBoxed.connectCharValue(TOPIC_BOXED_CHARACTER);
+
+    sender = new MqttUpdater().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
+    assertTrue(sender.waitUntilReady(2, TimeUnit.SECONDS));
+
+    final int expectedIntValue = 1;
+    final short expectedShortValue = 2;
+    final long expectedLongValue = 3L;
+    final float expectedFloatValue = 4.1f;
+    final double expectedDoubleValue = 5.2;
+    final char expectedCharValue = 'c';
+    final String expectedStringValue = "6.3";
+
+    sender.publish(TOPIC_NATIVE_INT, ByteBuffer.allocate(4).putInt(expectedIntValue).array());
+    sender.publish(TOPIC_NATIVE_SHORT, ByteBuffer.allocate(2).putShort(expectedShortValue).array());
+    sender.publish(TOPIC_NATIVE_LONG, ByteBuffer.allocate(8).putLong(expectedLongValue).array());
+    sender.publish(TOPIC_NATIVE_FLOAT, ByteBuffer.allocate(4).putFloat(expectedFloatValue).array());
+    sender.publish(TOPIC_NATIVE_DOUBLE, ByteBuffer.allocate(8).putDouble(expectedDoubleValue).array());
+    sender.publish(TOPIC_NATIVE_CHAR, ByteBuffer.allocate(2).putChar(expectedCharValue).array());
+    sender.publish(TOPIC_NATIVE_STRING, expectedStringValue.getBytes());
+
+    sender.publish(TOPIC_BOXED_INTEGER, ByteBuffer.allocate(4).putInt(expectedIntValue).array());
+    sender.publish(TOPIC_BOXED_SHORT, ByteBuffer.allocate(2).putShort(expectedShortValue).array());
+    sender.publish(TOPIC_BOXED_LONG, ByteBuffer.allocate(8).putLong(expectedLongValue).array());
+    sender.publish(TOPIC_BOXED_FLOAT, ByteBuffer.allocate(4).putFloat(expectedFloatValue).array());
+    sender.publish(TOPIC_BOXED_DOUBLE, ByteBuffer.allocate(8).putDouble(expectedDoubleValue).array());
+    sender.publish(TOPIC_BOXED_CHARACTER, ByteBuffer.allocate(2).putChar(expectedCharValue).array());
+
+    TimeUnit.SECONDS.sleep(2);
+
+    assertEquals(expectedIntValue, integers.getIntValue());
+    assertEquals(expectedShortValue, integers.getShortValue());
+    assertEquals(expectedLongValue, integers.getLongValue());
+    assertEquals(expectedFloatValue, floats.getFloatValue(), TestUtils.DELTA);
+    assertEquals(expectedDoubleValue, floats.getDoubleValue(), TestUtils.DELTA);
+    assertEquals(expectedCharValue, chars.getCharValue());
+    assertEquals(expectedStringValue, chars.getStringValue());
+
+    assertEquals(expectedIntValue, allBoxed.getIntValue().intValue());
+    assertEquals(expectedShortValue, allBoxed.getShortValue().shortValue());
+    assertEquals(expectedLongValue, allBoxed.getLongValue().longValue());
+    assertEquals(expectedFloatValue, allBoxed.getFloatValue(), TestUtils.DELTA);
+    assertEquals(expectedDoubleValue, allBoxed.getDoubleValue(), TestUtils.DELTA);
+    assertEquals(expectedCharValue, allBoxed.getCharValue().charValue());
+  }
+
+  private void createModel() {
+    model = new A();
+    integers = new NativeTypes();
+    model.addNativeTypes(integers);
+    floats = new NativeTypes();
+    model.addNativeTypes(floats);
+    chars = new NativeTypes();
+    model.addNativeTypes(chars);
+    allBoxed = new BoxedTypes();
+    model.addBoxedTypes(allBoxed);
+  }
+
+}
diff --git a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java
index d70f89378395d32ff0b88cc0be72fab72e85f84c..c58eda7703dad37f824b7faa6c6df036d3ac869b 100644
--- a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java
+++ b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java
@@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 import static org.junit.Assert.*;
@@ -21,20 +20,21 @@ import static org.junit.Assert.*;
  */
 public class ExampleTest {
 
-  private static final double DELTA = 0.001d;
   private static final String TOPIC_CONFIG = "robot/config";
   private static final String TOPIC_JOINT1 = "robot/arm/joint1";
 
   private Model model;
   private RobotArm robotArm;
   private Joint joint1;
-
-  private CountDownLatch stop;
+  private MqttUpdater receiver;
 
   @AfterEach
-  public void signalCondition() {
-    if (stop != null) {
-      stop.countDown();
+  public void closeConnections() {
+    if (receiver != null) {
+      receiver.close();
+    }
+    if (model != null) {
+      model.MqttCloseConnections();
     }
   }
 
@@ -48,10 +48,7 @@ public class ExampleTest {
     createModel();
     List<RobotConfig> receivedConfigs = new ArrayList<>();
 
-    CountDownLatch start = new CountDownLatch(1);
-    stop = new CountDownLatch(1);
-    createListenerThread(receivedConfigs, start, stop);
-    start.await();
+    createListener(receivedConfigs);
 
     model.MqttSetHost(TestUtils.getMqttHost());
     assertTrue(model.MqttWaitUntilReady(2, TimeUnit.SECONDS));
@@ -68,39 +65,28 @@ public class ExampleTest {
     // there should be two configs received by now (the initial one, and the updated)
     assertEquals(2, receivedConfigs.size());
     RobotConfig actualInitialConfig = receivedConfigs.get(0);
-    assertEquals(robotArm.speedLow(), actualInitialConfig.getSpeed(), DELTA);
+    assertEquals(robotArm.speedLow(), actualInitialConfig.getSpeed(), TestUtils.DELTA);
 
     RobotConfig actualUpdatedConfig = receivedConfigs.get(1);
-    assertEquals(robotArm.speedHigh(), actualUpdatedConfig.getSpeed(), DELTA);
-    model.MqttCloseConnections();
+    assertEquals(robotArm.speedHigh(), actualUpdatedConfig.getSpeed(), TestUtils.DELTA);
   }
 
-  private void createListenerThread(List<RobotConfig> receivedConfigs,
-                                    CountDownLatch startCondition, CountDownLatch stopCondition) {
-    new Thread(() -> {
-      MqttUpdater receiver = new MqttUpdater("receiver");
-      try {
-        receiver.setHost(TestUtils.getMqttHost(), TestUtils.getMqttDefaultPort());
-      } catch (IOException e) {
-        fail("Could not set host: " + e.getMessage());
-      }
-      assertTrue(receiver.waitUntilReady(2, TimeUnit.SECONDS));
-      receiver.newConnection(TOPIC_CONFIG, bytes -> {
-        try {
-          RobotConfig config = RobotConfig.parseFrom(bytes);
-          receivedConfigs.add(config);
-        } catch (InvalidProtocolBufferException e) {
-          fail("Received bad config: " + e.getMessage());
-        }
-      });
-      startCondition.countDown();
+  private void createListener(List<RobotConfig> receivedConfigs) {
+    receiver = new MqttUpdater("receiver");
+    try {
+      receiver.setHost(TestUtils.getMqttHost(), TestUtils.getMqttDefaultPort());
+    } catch (IOException e) {
+      fail("Could not set host: " + e.getMessage());
+    }
+    assertTrue(receiver.waitUntilReady(2, TimeUnit.SECONDS));
+    receiver.newConnection(TOPIC_CONFIG, bytes -> {
       try {
-        stopCondition.await();
-        receiver.close();
-      } catch (InterruptedException e) {
-        e.printStackTrace();
+        RobotConfig config = RobotConfig.parseFrom(bytes);
+        receivedConfigs.add(config);
+      } catch (InvalidProtocolBufferException e) {
+        fail("Received bad config: " + e.getMessage());
       }
-    }).start();
+    });
   }
 
   private void createModel() {
diff --git a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java
index 083b9982cfce12243080dd10d75b15a22dd2c8cf..e910f3b13c47cef53cefb3206aefa64fa2514c29 100644
--- a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java
+++ b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java
@@ -7,6 +7,8 @@ package org.jastadd.ros2rag.tests;
  */
 public class TestUtils {
 
+  public static final double DELTA = 0.001d;
+
   public static String getMqttHost() {
     if (System.getenv("GITLAB_CI") != null) {
       // we are in the CI, so use "mqtt" as host