Merge pull request #463 from theunkn0wn1/feature/grpcio_tools_protoc

use grpcio-tools protoc instead of system protoc
diff --git a/README.md b/README.md
index 16d77ab..501675d 100644
--- a/README.md
+++ b/README.md
@@ -82,9 +82,13 @@
 
 Note: Mac OS X by default aliases 'clang' as 'gcc', while not actually
 supporting the same command line options as gcc does. To run tests on
-Mac OS X, use: "scons CC=clang CXX=clang". Same way can be used to run
+Mac OS X, use: `scons CC=clang CXX=clang`. Same way can be used to run
 tests with different compilers on any platform.
 
+For embedded platforms, there is currently support for running the tests
+on STM32 discovery board and [simavr](https://github.com/buserror/simavr)
+AVR simulator. Use `scons PLATFORM=STM32` and `scons PLATFORM=AVR` to run
+these tests.
 
 
 Build systems and integration
diff --git a/docs/reference.rst b/docs/reference.rst
index 89d1f0f..39bc611 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -59,6 +59,8 @@
                                This is only to be used when the decoder on the
                                receiving side cannot process packed scalar
                                arrays. Such example is older protobuf.js.
+PB_CONVERT_DOUBLE_FLOAT        Convert doubles to floats for platforms that do
+                               not support 64-bit doubles. Mainly AVR.
 ============================  ================================================
 
 The PB_MAX_REQUIRED_FIELDS, PB_FIELD_16BIT and PB_FIELD_32BIT settings allow
diff --git a/examples/using_double_on_avr/Makefile b/examples/using_double_on_avr/Makefile
deleted file mode 100644
index 874a64b..0000000
--- a/examples/using_double_on_avr/Makefile
+++ /dev/null
@@ -1,24 +0,0 @@
-# Include the nanopb provided Makefile rules
-include ../../extra/nanopb.mk
-
-# Compiler flags to enable all warnings & debug info
-CFLAGS = -Wall -Werror -g -O0
-CFLAGS += -I$(NANOPB_DIR)
-
-all: run_tests
-
-.SUFFIXES:
-
-clean:
-	rm -f test_conversions encode_double decode_double doubleproto.pb.c doubleproto.pb.h
-
-test_conversions: test_conversions.c double_conversion.c
-	$(CC) $(CFLAGS) -o $@ $^
-
-%: %.c double_conversion.c doubleproto.pb.c
-	$(CC) $(CFLAGS) -o $@ $^ $(NANOPB_CORE)
-
-run_tests: test_conversions encode_double decode_double
-	./test_conversions
-	./encode_double | ./decode_double
-    
diff --git a/examples/using_double_on_avr/README.txt b/examples/using_double_on_avr/README.txt
deleted file mode 100644
index d9fcdfc..0000000
--- a/examples/using_double_on_avr/README.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-Nanopb example "using_double_on_avr"
-====================================
-
-Some processors/compilers, such as AVR-GCC, do not support the double
-datatype. Instead, they have sizeof(double) == 4. Because protocol
-binary format uses the double encoding directly, this causes trouble
-if the protocol in .proto requires double fields.
-
-This directory contains a solution to this problem. It uses uint64_t
-to store the raw wire values, because its size is correct on all
-platforms. The file double_conversion.c provides functions that
-convert these values to/from floats, without relying on compiler
-support.
-
-To use this method, you need to make some modifications to your code:
-
-1) Change all 'double' fields into 'fixed64' in the .proto.
-
-2) Whenever writing to a 'double' field, use float_to_double().
-
-3) Whenever reading a 'double' field, use double_to_float().
-
-The conversion routines are as accurate as the float datatype can
-be. Furthermore, they should handle all special values (NaN, inf, denormalized
-numbers) correctly. There are testcases in test_conversions.c.
diff --git a/examples/using_double_on_avr/decode_double.c b/examples/using_double_on_avr/decode_double.c
deleted file mode 100644
index 5802eca..0000000
--- a/examples/using_double_on_avr/decode_double.c
+++ /dev/null
@@ -1,33 +0,0 @@
-/* Decodes a double value into a float variable.
- * Used to read double values with AVR code, which doesn't support double directly.
- */
-
-#include <stdio.h>
-#include <pb_decode.h>
-#include "double_conversion.h"
-#include "doubleproto.pb.h"
-
-int main()
-{
-    uint8_t buffer[32];
-    size_t count = fread(buffer, 1, sizeof(buffer), stdin);
-    pb_istream_t stream = pb_istream_from_buffer(buffer, count);
-    
-    AVRDoubleMessage message;
-    pb_decode(&stream, AVRDoubleMessage_fields, &message);
-    
-    float v1 = double_to_float(message.field1);
-    float v2 = double_to_float(message.field2);
-
-    printf("Values: %f %f\n", v1, v2);
-    
-    if (v1 == 1234.5678f &&
-        v2 == 0.00001f)
-    {
-        return 0;
-    }
-    else
-    {
-        return 1;
-    }
-}
diff --git a/examples/using_double_on_avr/double_conversion.c b/examples/using_double_on_avr/double_conversion.c
deleted file mode 100644
index cf79b9a..0000000
--- a/examples/using_double_on_avr/double_conversion.c
+++ /dev/null
@@ -1,123 +0,0 @@
-/* Conversion routines for platforms that do not support 'double' directly. */
-
-#include "double_conversion.h"
-#include <math.h>
-
-typedef union {
-    float f;
-    uint32_t i;
-} conversion_t;
-
-/* Note: IEE 754 standard specifies float formats as follows:
- * Single precision: sign,  8-bit exp, 23-bit frac.
- * Double precision: sign, 11-bit exp, 52-bit frac.
- */
-
-uint64_t float_to_double(float value)
-{
-    conversion_t in;
-    in.f = value;
-    uint8_t sign;
-    int16_t exponent;
-    uint64_t mantissa;
-    
-    /* Decompose input value */
-    sign = (in.i >> 31) & 1;
-    exponent = ((in.i >> 23) & 0xFF) - 127;
-    mantissa = in.i & 0x7FFFFF;
-    
-    if (exponent == 128)
-    {
-        /* Special value (NaN etc.) */
-        exponent = 1024;
-    }
-    else if (exponent == -127)
-    {
-        if (!mantissa)
-        {
-            /* Zero */
-            exponent = -1023;
-        }
-        else
-        {
-            /* Denormalized */
-            mantissa <<= 1;
-            while (!(mantissa & 0x800000))
-            {
-                mantissa <<= 1;
-                exponent--;
-            }
-            mantissa &= 0x7FFFFF;
-        }
-    }
-    
-    /* Combine fields */
-    mantissa <<= 29;
-    mantissa |= (uint64_t)(exponent + 1023) << 52;
-    mantissa |= (uint64_t)sign << 63;
-    
-    return mantissa;
-}
-
-float double_to_float(uint64_t value)
-{
-    uint8_t sign;
-    int16_t exponent;
-    uint32_t mantissa;
-    conversion_t out;
-
-    /* Decompose input value */
-    sign = (value >> 63) & 1;
-    exponent = ((value >> 52) & 0x7FF) - 1023;
-    mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */
- 
-    /* Figure if value is in range representable by floats. */
-    if (exponent == 1024)
-    {
-        /* Special value */
-        exponent = 128;
-    }
-    else if (exponent > 127)
-    {
-        /* Too large */        
-        if (sign)
-            return -INFINITY;
-        else
-            return INFINITY;
-    }
-    else if (exponent < -150)
-    {
-        /* Too small */
-        if (sign)
-            return -0.0f;
-        else
-            return 0.0f;
-    }
-    else if (exponent < -126)
-    {
-        /* Denormalized */
-        mantissa |= 0x1000000;
-        mantissa >>= (-126 - exponent);
-        exponent = -127;
-    }
- 
-    /* Round off mantissa */
-    mantissa = (mantissa + 1) >> 1;
-    
-    /* Check if mantissa went over 2.0 */
-    if (mantissa & 0x800000)
-    {
-        exponent += 1;
-        mantissa &= 0x7FFFFF;
-        mantissa >>= 1;
-    }
-    
-    /* Combine fields */
-    out.i = mantissa;
-    out.i |= (uint32_t)(exponent + 127) << 23;
-    out.i |= (uint32_t)sign << 31;
-    
-    return out.f;
-}
-
-
diff --git a/examples/using_double_on_avr/double_conversion.h b/examples/using_double_on_avr/double_conversion.h
deleted file mode 100644
index 62b6a8a..0000000
--- a/examples/using_double_on_avr/double_conversion.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* AVR-GCC does not have real double datatype. Instead its double
- * is equal to float, i.e. 32 bit value. If you need to communicate
- * with other systems that use double in their .proto files, you
- * need to do some conversion.
- *
- * These functions use bitwise operations to mangle floats into doubles
- * and then store them in uint64_t datatype.
- */
-
-#ifndef DOUBLE_CONVERSION
-#define DOUBLE_CONVERSION
-
-#include <stdint.h>
-
-/* Convert native 4-byte float into a 8-byte double. */
-extern uint64_t float_to_double(float value);
-
-/* Convert 8-byte double into native 4-byte float.
- * Values are rounded to nearest, 0.5 away from zero.
- * Overflowing values are converted to Inf or -Inf.
- */
-extern float double_to_float(uint64_t value);
-
-
-#endif
-
diff --git a/examples/using_double_on_avr/doubleproto.proto b/examples/using_double_on_avr/doubleproto.proto
deleted file mode 100644
index 72d3f9c..0000000
--- a/examples/using_double_on_avr/doubleproto.proto
+++ /dev/null
@@ -1,15 +0,0 @@
-// A message containing doubles, as used by other applications.
-syntax = "proto2";
-
-message DoubleMessage {
-    required double field1 = 1;
-    required double field2 = 2;
-}
-
-// A message containing doubles, but redefined using uint64_t.
-// For use in AVR code.
-message AVRDoubleMessage {
-    required fixed64 field1 = 1;
-    required fixed64 field2 = 2;
-}
-
diff --git a/examples/using_double_on_avr/encode_double.c b/examples/using_double_on_avr/encode_double.c
deleted file mode 100644
index cd532d4..0000000
--- a/examples/using_double_on_avr/encode_double.c
+++ /dev/null
@@ -1,25 +0,0 @@
-/* Encodes a float value into a double on the wire.
- * Used to emit doubles from AVR code, which doesn't support double directly.
- */
-
-#include <stdio.h>
-#include <pb_encode.h>
-#include "double_conversion.h"
-#include "doubleproto.pb.h"
-
-int main()
-{
-    AVRDoubleMessage message = {
-        float_to_double(1234.5678f),
-        float_to_double(0.00001f)
-    };
-    
-    uint8_t buffer[32];
-    pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
-    
-    pb_encode(&stream, AVRDoubleMessage_fields, &message);
-    fwrite(buffer, 1, stream.bytes_written, stdout);
-
-    return 0;
-}
-
diff --git a/examples/using_double_on_avr/test_conversions.c b/examples/using_double_on_avr/test_conversions.c
deleted file mode 100644
index 22620a6..0000000
--- a/examples/using_double_on_avr/test_conversions.c
+++ /dev/null
@@ -1,56 +0,0 @@
-#include "double_conversion.h"
-#include <math.h>
-#include <stdio.h>
-
-static const double testvalues[] = {
-           0.0,        -0.0,         0.1,         -0.1,
-          M_PI,       -M_PI,  123456.789,  -123456.789,
-      INFINITY,   -INFINITY,         NAN, INFINITY - INFINITY,
-          1e38,       -1e38,        1e39,        -1e39,
-         1e-38,      -1e-38,       1e-39,       -1e-39,
-   3.14159e-37,-3.14159e-37, 3.14159e-43, -3.14159e-43,
-         1e-60,      -1e-60,       1e-45,       -1e-45,
-    0.99999999999999, -0.99999999999999, 127.999999999999, -127.999999999999
-};
-
-#define TESTVALUES_COUNT (sizeof(testvalues)/sizeof(testvalues[0]))
-
-int main()
-{
-    int status = 0;
-    int i;
-    for (i = 0; i < TESTVALUES_COUNT; i++)
-    {
-        double orig = testvalues[i];
-        float expected_float = (float)orig;
-        double expected_double = (double)expected_float;
-        
-        float got_float = double_to_float(*(uint64_t*)&orig);
-        uint64_t got_double = float_to_double(got_float);
-        
-        uint32_t e1 = *(uint32_t*)&expected_float;
-        uint32_t g1 = *(uint32_t*)&got_float;
-        uint64_t e2 = *(uint64_t*)&expected_double;
-        uint64_t g2 = got_double;
-        
-        if (g1 != e1)
-        {
-            printf("%3d double_to_float fail: %08x != %08x\n", i, g1, e1);
-            status = 1;
-        }
-        
-        if (g2 != e2)
-        {
-            printf("%3d float_to_double fail: %016llx != %016llx\n", i,
-                (unsigned long long)g2,
-                (unsigned long long)e2);
-            status = 1;
-        }
-    }
-
-    return status;
-}
-
-    
-    
-
diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py
index 65acd9c..87802cd 100755
--- a/generator/nanopb_generator.py
+++ b/generator/nanopb_generator.py
@@ -1475,7 +1475,6 @@
 
             if [msg for msg in self.messages if hasattr(msg,'msgid')]:
               yield '/* Message IDs (where set with "msgid" option) */\n'
-              yield '#ifdef PB_MSGID\n'
               for msg in self.messages:
                   if hasattr(msg,'msgid'):
                       yield '#define PB_MSG_%d %s\n' % (msg.msgid, msg.name)
@@ -1497,7 +1496,6 @@
                   if hasattr(msg,'msgid'):
                       yield '#define %s_msgid %d\n' % (msg.name, msg.msgid)
               yield '\n'
-              yield '#endif\n\n'
 
         yield '#ifdef __cplusplus\n'
         yield '} /* extern "C" */\n'
@@ -1565,12 +1563,13 @@
 
         if has_double:
             yield '\n'
+            yield '#ifndef PB_CONVERT_DOUBLE_FLOAT\n'
             yield '/* On some platforms (such as AVR), double is really float.\n'
-            yield ' * Using double on these platforms is not directly supported\n'
-            yield ' * by nanopb, but see example_avr_double.\n'
-            yield ' * To get rid of this error, remove any double fields from your .proto.\n'
+            yield ' * To be able to encode/decode double on these platforms, you need.\n'
+            yield ' * to define PB_CONVERT_DOUBLE_FLOAT in pb.h or compiler command line.\n'
             yield ' */\n'
             yield 'PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES)\n'
+            yield '#endif\n'
 
         yield '\n'
         yield '/* @@protoc_insertion_point(eof) */\n'
diff --git a/pb.h b/pb.h
index f9d368e..74cc7e9 100644
--- a/pb.h
+++ b/pb.h
@@ -45,6 +45,10 @@
  * Such example is older protobuf.js. */
 /* #define PB_ENCODE_ARRAYS_UNPACKED 1 */
 
+/* Enable conversion of doubles to floats for platforms that do not
+ * support 64-bit doubles. Most commonly AVR. */
+/* #define PB_CONVERT_DOUBLE_FLOAT 1 */
+
 /******************************************************************
  * You usually don't need to change anything below this line.     *
  * Feel free to look around and use the defined macros, though.   *
diff --git a/pb_decode.c b/pb_decode.c
index ac586e7..62a7d97 100644
--- a/pb_decode.c
+++ b/pb_decode.c
@@ -274,7 +274,12 @@
     if (!pb_decode_varint32(stream, &length))
         return false;
     
-    return pb_read(stream, NULL, length);
+    if ((size_t)length != length)
+    {
+        PB_RETURN_ERROR(stream, "size too large");
+    }
+
+    return pb_read(stream, NULL, (size_t)length);
 }
 
 bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof)
@@ -358,8 +363,8 @@
     if (substream->bytes_left < size)
         PB_RETURN_ERROR(stream, "parent stream too short");
     
-    substream->bytes_left = size;
-    stream->bytes_left -= size;
+    substream->bytes_left = (size_t)size;
+    stream->bytes_left -= (size_t)size;
     return true;
 }
 
@@ -476,7 +481,7 @@
             {
                 /* We memset to zero so that any callbacks are set to NULL.
                  * pb_dec_submessage() will set any default values. */
-                memset(field->pData, 0, field->data_size);
+                memset(field->pData, 0, (size_t)field->data_size);
             }
             return decode_basic_field(stream, field);
 
@@ -851,7 +856,7 @@
             else
             {
                 /* Initialize to zeros */
-                memset(field->pData, 0, field->data_size);
+                memset(field->pData, 0, (size_t)field->data_size);
             }
         }
     }
@@ -1039,9 +1044,9 @@
          * seeking to the end of the field array. Usually we
          * are already close to end after decoding.
          */
-        unsigned req_field_count;
+        pb_size_t req_field_count;
         pb_type_t last_type;
-        unsigned i;
+        pb_size_t i;
         do {
             req_field_count = iter.required_field_index;
             last_type = iter.type;
@@ -1067,7 +1072,7 @@
             if ((req_field_count & 31) != 0)
             {
                 if (fields_seen.bitfield[req_field_count >> 5] !=
-                    (allbits >> (32 - (req_field_count & 31))))
+                    (allbits >> (uint8_t)(32 - (req_field_count & 31))))
                 {
                     PB_RETURN_ERROR(stream, "missing required field");
                 }
@@ -1410,6 +1415,13 @@
 
 static bool checkreturn pb_dec_fixed(pb_istream_t *stream, const pb_field_iter_t *field)
 {
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+    if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64)
+    {
+        return pb_decode_double_as_float(stream, (float*)field->pData);
+    }
+#endif
+
     if (field->data_size == sizeof(uint32_t))
     {
         return pb_decode_fixed32(stream, field->pData);
@@ -1460,7 +1472,7 @@
     }
 
     dest->size = (pb_size_t)size;
-    return pb_read(stream, dest->bytes, size);
+    return pb_read(stream, dest->bytes, (size_t)size);
 }
 
 static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field)
@@ -1473,7 +1485,7 @@
         return false;
     
     /* Space for null terminator */
-    alloc_size = size + 1;
+    alloc_size = (size_t)(size + 1);
     
     if (alloc_size < size)
         PB_RETURN_ERROR(stream, "size too large");
@@ -1495,7 +1507,7 @@
     }
     
     dest[size] = 0;
-    return pb_read(stream, dest, size);
+    return pb_read(stream, dest, (size_t)size);
 }
 
 static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field)
@@ -1536,13 +1548,76 @@
     if (size == 0)
     {
         /* As a special case, treat empty bytes string as all zeros for fixed_length_bytes. */
-        memset(field->pData, 0, field->data_size);
+        memset(field->pData, 0, (size_t)field->data_size);
         return true;
     }
 
     if (size != field->data_size)
         PB_RETURN_ERROR(stream, "incorrect fixed length bytes size");
 
-    return pb_read(stream, (pb_byte_t*)field->pData, field->data_size);
+    return pb_read(stream, (pb_byte_t*)field->pData, (size_t)field->data_size);
 }
 
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+bool pb_decode_double_as_float(pb_istream_t *stream, float *dest)
+{
+    uint8_t sign;
+    int exponent;
+    uint32_t mantissa;
+    uint64_t value;
+    union { float f; uint32_t i; } out;
+
+    if (!pb_decode_fixed64(stream, &value))
+        return false;
+
+    /* Decompose input value */
+    sign = (uint8_t)((value >> 63) & 1);
+    exponent = (int)((value >> 52) & 0x7FF) - 1023;
+    mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */
+
+    /* Figure if value is in range representable by floats. */
+    if (exponent == 1024)
+    {
+        /* Special value */
+        exponent = 128;
+    }
+    else if (exponent > 127)
+    {
+        /* Too large, convert to infinity */
+        exponent = 128;
+        mantissa = 0;
+    }
+    else if (exponent < -150)
+    {
+        /* Too small, convert to zero */
+        exponent = -127;
+        mantissa = 0;
+    }
+    else if (exponent < -126)
+    {
+        /* Denormalized */
+        mantissa |= 0x1000000;
+        mantissa >>= (-126 - exponent);
+        exponent = -127;
+    }
+
+    /* Round off mantissa */
+    mantissa = (mantissa + 1) >> 1;
+
+    /* Check if mantissa went over 2.0 */
+    if (mantissa & 0x800000)
+    {
+        exponent += 1;
+        mantissa &= 0x7FFFFF;
+        mantissa >>= 1;
+    }
+
+    /* Combine fields */
+    out.i = mantissa;
+    out.i |= (uint32_t)(exponent + 127) << 23;
+    out.i |= (uint32_t)sign << 31;
+
+    *dest = out.f;
+    return true;
+}
+#endif
diff --git a/pb_decode.h b/pb_decode.h
index 0498a83..b64d95a 100644
--- a/pb_decode.h
+++ b/pb_decode.h
@@ -177,6 +177,11 @@
 bool pb_decode_fixed64(pb_istream_t *stream, void *dest);
 #endif
 
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+/* Decode a double value into float variable. */
+bool pb_decode_double_as_float(pb_istream_t *stream, float *dest);
+#endif
+
 /* Make a limited-length substream for reading a PB_WT_STRING field. */
 bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream);
 bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream);
diff --git a/pb_encode.c b/pb_encode.c
index 8856981..093173b 100644
--- a/pb_encode.c
+++ b/pb_encode.c
@@ -795,6 +795,13 @@
 
 static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field)
 {
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+    if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64)
+    {
+        return pb_encode_float_as_double(stream, *(float*)field->pData);
+    }
+#endif
+
     if (field->data_size == sizeof(uint32_t))
     {
         return pb_encode_fixed32(stream, field->pData);
@@ -829,13 +836,13 @@
         PB_RETURN_ERROR(stream, "bytes size exceeded");
     }
     
-    return pb_encode_string(stream, bytes->bytes, bytes->size);
+    return pb_encode_string(stream, bytes->bytes, (size_t)bytes->size);
 }
 
 static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field)
 {
     size_t size = 0;
-    size_t max_size = field->data_size;
+    size_t max_size = (size_t)field->data_size;
     const char *str = (const char*)field->pData;
     
     if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
@@ -890,5 +897,54 @@
 
 static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field)
 {
-    return pb_encode_string(stream, (const pb_byte_t*)field->pData, field->data_size);
+    return pb_encode_string(stream, (const pb_byte_t*)field->pData, (size_t)field->data_size);
 }
+
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+bool pb_encode_float_as_double(pb_ostream_t *stream, float value)
+{
+    union { float f; uint32_t i; } in;
+    uint8_t sign;
+    int exponent;
+    uint64_t mantissa;
+
+    in.f = value;
+
+    /* Decompose input value */
+    sign = (uint8_t)((in.i >> 31) & 1);
+    exponent = (int)((in.i >> 23) & 0xFF) - 127;
+    mantissa = in.i & 0x7FFFFF;
+
+    if (exponent == 128)
+    {
+        /* Special value (NaN etc.) */
+        exponent = 1024;
+    }
+    else if (exponent == -127)
+    {
+        if (!mantissa)
+        {
+            /* Zero */
+            exponent = -1023;
+        }
+        else
+        {
+            /* Denormalized */
+            mantissa <<= 1;
+            while (!(mantissa & 0x800000))
+            {
+                mantissa <<= 1;
+                exponent--;
+            }
+            mantissa &= 0x7FFFFF;
+        }
+    }
+
+    /* Combine fields */
+    mantissa <<= 29;
+    mantissa |= (uint64_t)(exponent + 1023) << 52;
+    mantissa |= (uint64_t)sign << 63;
+
+    return pb_encode_fixed64(stream, &mantissa);
+}
+#endif
diff --git a/pb_encode.h b/pb_encode.h
index 66f6b2f..88e246a 100644
--- a/pb_encode.h
+++ b/pb_encode.h
@@ -165,6 +165,12 @@
 bool pb_encode_fixed64(pb_ostream_t *stream, const void *value);
 #endif
 
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+/* Encode a float value so that it appears like a double in the encoded
+ * message. */
+bool pb_encode_float_as_double(pb_ostream_t *stream, float value);
+#endif
+
 /* Encode a submessage field.
  * You need to pass the pb_field_t array and pointer to struct, just like
  * with pb_encode(). This internally encodes the submessage twice, first to
diff --git a/tests/SConstruct b/tests/SConstruct
index 0158b1d..18c1b04 100644
--- a/tests/SConstruct
+++ b/tests/SConstruct
@@ -8,6 +8,7 @@
 CXX         Name of C++ compiler
 CCFLAGS     Flags to pass to the C compiler
 CXXFLAGS    Flags to pass to the C++ compiler
+LINKFLAGS   Flags to pass to linker
 
 For example, for a clang build, use:
 scons CC=clang CXX=clang++
@@ -18,6 +19,8 @@
 
 if ARGUMENTS.get('PLATFORM') == 'STM32':
     set_stm32_platform(env)
+elif ARGUMENTS.get('PLATFORM') == 'AVR':
+    set_avr_platform(env)
 
 # Limit memory usage. This is to catch problems like issue #338
 try:
@@ -32,6 +35,7 @@
 if 'CXX' in ARGUMENTS: env.Replace(CXX = ARGUMENTS['CXX'])
 if 'CCFLAGS' in ARGUMENTS: env.Append(CCFLAGS = ARGUMENTS['CCFLAGS'])
 if 'CXXFLAGS' in ARGUMENTS: env.Append(CXXFLAGS = ARGUMENTS['CXXFLAGS'])
+if 'LINKFLAGS' in ARGUMENTS: env.Append(LINKFLAGS = ARGUMENTS['LINKFLAGS'])
 
 # Add the builders defined in site_init.py
 add_nanopb_builders(env)
@@ -122,9 +126,14 @@
     # GNU Compiler Collection
     
     # Debug info, warnings as errors
-    env.Append(CFLAGS = '-ansi -pedantic -g -Wall -Werror ')
+    env.Append(CFLAGS = '-g -Wall -Werror ')
     env.Append(CORECFLAGS = '-Wextra')
     
+    # Pedantic ANSI C. On AVR this doesn't work because we use large
+    # enums in some of the tests.
+    if env.get("EMBEDDED") != "AVR":
+        env.Append(CFLAGS = '-ansi -pedantic')
+    
     # Profiling and coverage
     if not env.get("EMBEDDED"):
         env.Append(CFLAGS = '-fprofile-arcs -ftest-coverage ')
@@ -163,7 +172,7 @@
 elif 'g++' in env['CXX'] or 'gcc' in env['CXX']:
     env.Append(CXXFLAGS = '-g -Wall -Werror -Wextra -Wno-missing-field-initializers')
 elif 'cl' in env['CXX']:
-    env.Append(CXXFLAGS = '/Zi /W2 /WX /wd4116')
+    env.Append(CXXFLAGS = '/Zi /W2 /WX /wd4116 /wd4127')
 
 if not env.get("EMBEDDED"):
     valgrind = env.WhereIs('valgrind')
diff --git a/tests/alltypes/decode_alltypes.c b/tests/alltypes/decode_alltypes.c
index f00e1a7..532694e 100644
--- a/tests/alltypes/decode_alltypes.c
+++ b/tests/alltypes/decode_alltypes.c
@@ -17,7 +17,7 @@
 {
     int status = 0;
 
-    /* Uses _init_default to just make sure that it works. */
+    /* Uses _init_default to just make sure that the macro works. */
     AllTypes alltypes = AllTypes_init_default;
     
     /* Fill with garbage to better detect initialization errors */
@@ -278,6 +278,9 @@
     return status == 0;
 }
 
+#ifdef __cplusplus
+extern "C"
+#endif
 int main(int argc, char **argv)
 {
     uint8_t buffer[1024];
diff --git a/tests/alltypes/encode_alltypes.c b/tests/alltypes/encode_alltypes.c
index 15ea7b8..c1c2aa3 100644
--- a/tests/alltypes/encode_alltypes.c
+++ b/tests/alltypes/encode_alltypes.c
@@ -8,6 +8,9 @@
 #include "alltypes.pb.h"
 #include "test_helpers.h"
 
+#ifdef __cplusplus
+extern "C"
+#endif
 int main(int argc, char **argv)
 {
     int mode = (argc > 1) ? atoi(argv[1]) : 0;
diff --git a/tests/alltypes_callback/decode_alltypes_callback.c b/tests/alltypes_callback/decode_alltypes_callback.c
index 576ce30..83f4968 100644
--- a/tests/alltypes_callback/decode_alltypes_callback.c
+++ b/tests/alltypes_callback/decode_alltypes_callback.c
@@ -21,7 +21,7 @@
     if (!pb_decode_varint(stream, &value))
         return false;
     
-    TEST((int64_t)value == (long)*arg);
+    TEST((int64_t)value == (intptr_t)*arg);
     return true;
 }
 
@@ -31,7 +31,7 @@
     if (!pb_decode_svarint(stream, &value))
         return false;
     
-    TEST(value == (long)*arg);
+    TEST(value == (intptr_t)*arg);
     return true;
 }
 
@@ -55,6 +55,28 @@
     return true;
 }
 
+static bool read_double(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+    if (sizeof(double) == sizeof(float))
+    {
+        float value;
+        if (!pb_decode_double_as_float(stream, &value))
+            return false;
+        
+        TEST(memcmp(&value, *arg, sizeof(float)) == 0);
+        return true;
+    }
+#endif
+
+    uint64_t value;
+    if (!pb_decode_fixed64(stream, &value))
+        return false;
+    
+    TEST(value == *(uint64_t*)*arg);
+    return true;
+}
+
 static bool read_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
 {
     uint8_t buf[16] = {0};
@@ -132,6 +154,30 @@
     return true;
 }
 
+static bool read_repeated_double(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+    if (sizeof(double) == sizeof(float))
+    {
+        float** expectedf = (float**)arg;
+        float value;
+        if (!pb_decode_double_as_float(stream, &value))
+            return false;
+        
+        TEST(memcmp(&value, (*expectedf)++, sizeof(float)) == 0);
+        return true;
+    }
+#endif
+
+    uint64_t** expected = (uint64_t**)arg;
+    uint64_t value;
+    if (!pb_decode_fixed64(stream, &value))
+        return false;
+
+    TEST(*(*expected)++ == value);
+    return true;
+}
+
 static bool read_repeated_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
 {
     uint8_t*** expected = (uint8_t***)arg;
@@ -269,7 +315,7 @@
     alltypes.req_sfixed64.funcs.decode = &read_fixed64;
     alltypes.req_sfixed64.arg = &req_sfixed64;
     
-    alltypes.req_double.funcs.decode = &read_fixed64;
+    alltypes.req_double.funcs.decode = &read_double;
     alltypes.req_double.arg = &req_double;
     
     alltypes.req_string.funcs.decode = &read_string;
@@ -323,7 +369,7 @@
     alltypes.rep_sfixed64.funcs.decode = &read_repeated_fixed64;
     alltypes.rep_sfixed64.arg = rep_sfixed64;
     
-    alltypes.rep_double.funcs.decode = &read_repeated_fixed64;
+    alltypes.rep_double.funcs.decode = &read_repeated_double;
     alltypes.rep_double.arg = rep_double;
     
     alltypes.rep_string.funcs.decode = &read_repeated_string;
@@ -384,7 +430,7 @@
         alltypes.opt_sfixed64.funcs.decode = &read_fixed64;
         alltypes.opt_sfixed64.arg = &opt_sfixed64;
         
-        alltypes.opt_double.funcs.decode = &read_fixed64;
+        alltypes.opt_double.funcs.decode = &read_double;
         alltypes.opt_double.arg = &opt_double;
         
         alltypes.opt_string.funcs.decode = &read_string;
diff --git a/tests/alltypes_callback/encode_alltypes_callback.c b/tests/alltypes_callback/encode_alltypes_callback.c
index b206783..5f75256 100644
--- a/tests/alltypes_callback/encode_alltypes_callback.c
+++ b/tests/alltypes_callback/encode_alltypes_callback.c
@@ -13,13 +13,13 @@
 static bool write_varint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
 {
     return pb_encode_tag_for_field(stream, field) &&
-           pb_encode_varint(stream, (long)*arg);
+           pb_encode_varint(stream, (intptr_t)*arg);
 }
 
 static bool write_svarint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
 {
     return pb_encode_tag_for_field(stream, field) &&
-           pb_encode_svarint(stream, (long)*arg);
+           pb_encode_svarint(stream, (intptr_t)*arg);
 }
 
 static bool write_fixed32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
@@ -34,6 +34,18 @@
            pb_encode_fixed64(stream, *arg);
 }
 
+static bool write_double(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+    if (sizeof(double) == sizeof(float))
+        return pb_encode_tag_for_field(stream, field) &&
+               pb_encode_float_as_double(stream, *(float*)*arg);
+#endif
+
+    return pb_encode_tag_for_field(stream, field) &&
+           pb_encode_fixed64(stream, *arg);
+}
+
 static bool write_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
 {
     return pb_encode_tag_for_field(stream, field) &&
@@ -65,7 +77,7 @@
            pb_encode_tag_for_field(stream, field) &&
            pb_encode_varint(stream, 0) &&
            pb_encode_tag_for_field(stream, field) &&
-           pb_encode_varint(stream, (long)*arg);
+           pb_encode_varint(stream, (intptr_t)*arg);
 }
 
 static bool write_repeated_svarint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
@@ -79,7 +91,7 @@
            pb_encode_tag_for_field(stream, field) &&
            pb_encode_svarint(stream, 0) &&
            pb_encode_tag_for_field(stream, field) &&
-           pb_encode_svarint(stream, (long)*arg);
+           pb_encode_svarint(stream, (intptr_t)*arg);
 }
 
 static bool write_repeated_fixed32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
@@ -110,6 +122,31 @@
            pb_encode_fixed64(stream, *arg);
 }
 
+static bool write_repeated_double(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    uint64_t dummy = 0;
+
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+    if (sizeof(double) == sizeof(float))
+        return pb_encode_tag(stream, PB_WT_STRING, field->tag) &&
+               pb_encode_varint(stream, 5 * 8) && /* Number of bytes */
+               pb_encode_float_as_double(stream, 0.0f) &&
+               pb_encode_float_as_double(stream, 0.0f) &&
+               pb_encode_float_as_double(stream, 0.0f) &&
+               pb_encode_float_as_double(stream, 0.0f) &&
+               pb_encode_float_as_double(stream, *(float*)*arg);
+#endif
+
+    /* Make it a packed field */
+    return pb_encode_tag(stream, PB_WT_STRING, field->tag) &&
+           pb_encode_varint(stream, 5 * 8) && /* Number of bytes */
+           pb_encode_fixed64(stream, &dummy) &&
+           pb_encode_fixed64(stream, &dummy) &&
+           pb_encode_fixed64(stream, &dummy) &&
+           pb_encode_fixed64(stream, &dummy) &&
+           pb_encode_fixed64(stream, *arg);
+}
+
 static bool write_repeated_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
 {
     return pb_encode_tag_for_field(stream, field) &&
@@ -246,7 +283,7 @@
     alltypes.req_sfixed64.funcs.encode = &write_fixed64;
     alltypes.req_sfixed64.arg = &req_sfixed64;
     
-    alltypes.req_double.funcs.encode = &write_fixed64;
+    alltypes.req_double.funcs.encode = &write_double;
     alltypes.req_double.arg = &req_double;
     
     alltypes.req_string.funcs.encode = &write_string;
@@ -303,7 +340,7 @@
     alltypes.rep_sfixed64.funcs.encode = &write_repeated_fixed64;
     alltypes.rep_sfixed64.arg = &rep_sfixed64;
     
-    alltypes.rep_double.funcs.encode = &write_repeated_fixed64;
+    alltypes.rep_double.funcs.encode = &write_repeated_double;
     alltypes.rep_double.arg = &rep_double;
     
     alltypes.rep_string.funcs.encode = &write_repeated_string;
@@ -364,7 +401,7 @@
         alltypes.opt_sfixed64.funcs.encode = &write_fixed64;
         alltypes.opt_sfixed64.arg = &opt_sfixed64;
         
-        alltypes.opt_double.funcs.encode = &write_fixed64;
+        alltypes.opt_double.funcs.encode = &write_double;
         alltypes.opt_double.arg = &opt_double;
         
         alltypes.opt_string.funcs.encode = &write_string;
diff --git a/tests/alltypes_proto3_callback/decode_alltypes_callback.c b/tests/alltypes_proto3_callback/decode_alltypes_callback.c
index 2b3c2f3..ffa40aa 100644
--- a/tests/alltypes_proto3_callback/decode_alltypes_callback.c
+++ b/tests/alltypes_proto3_callback/decode_alltypes_callback.c
@@ -21,7 +21,7 @@
     if (!pb_decode_varint(stream, &value))
         return false;
     
-    TEST((int64_t)value == (long)*arg);
+    TEST((int64_t)value == (intptr_t)*arg);
     return true;
 }
 
@@ -31,7 +31,7 @@
     if (!pb_decode_svarint(stream, &value))
         return false;
     
-    TEST(value == (long)*arg);
+    TEST(value == (intptr_t)*arg);
     return true;
 }
 
@@ -55,6 +55,28 @@
     return true;
 }
 
+static bool read_double(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+    if (sizeof(double) == sizeof(float))
+    {
+        float value;
+        if (!pb_decode_double_as_float(stream, &value))
+            return false;
+        
+        TEST(memcmp(&value, *arg, sizeof(float)) == 0);
+        return true;
+    }
+#endif
+
+    uint64_t value;
+    if (!pb_decode_fixed64(stream, &value))
+        return false;
+    
+    TEST(value == *(uint64_t*)*arg);
+    return true;
+}
+
 static bool read_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
 {
     uint8_t buf[16] = {0};
@@ -131,6 +153,30 @@
     return true;
 }
 
+static bool read_repeated_double(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+    if (sizeof(double) == sizeof(float))
+    {
+        float** expectedf = (float**)arg;
+        float value;
+        if (!pb_decode_double_as_float(stream, &value))
+            return false;
+        
+        TEST(memcmp(&value, (*expectedf)++, sizeof(float)) == 0);
+        return true;
+    }
+#endif
+
+    uint64_t** expected = (uint64_t**)arg;
+    uint64_t value;
+    if (!pb_decode_fixed64(stream, &value))
+        return false;
+
+    TEST(*(*expected)++ == value);
+    return true;
+}
+
 static bool read_repeated_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
 {
     uint8_t*** expected = (uint8_t***)arg;
@@ -257,7 +303,7 @@
     alltypes.rep_sfixed64.funcs.decode = &read_repeated_fixed64;
     alltypes.rep_sfixed64.arg = rep_sfixed64;
     
-    alltypes.rep_double.funcs.decode = &read_repeated_fixed64;
+    alltypes.rep_double.funcs.decode = &read_repeated_double;
     alltypes.rep_double.arg = rep_double;
     
     alltypes.rep_string.funcs.decode = &read_repeated_string;
@@ -318,7 +364,7 @@
         alltypes.sng_sfixed64.funcs.decode = &read_fixed64;
         alltypes.sng_sfixed64.arg = &sng_sfixed64;
         
-        alltypes.sng_double.funcs.decode = &read_fixed64;
+        alltypes.sng_double.funcs.decode = &read_double;
         alltypes.sng_double.arg = &sng_double;
         
         alltypes.sng_string.funcs.decode = &read_string;
diff --git a/tests/alltypes_proto3_callback/encode_alltypes_callback.c b/tests/alltypes_proto3_callback/encode_alltypes_callback.c
index 8c7bdd6..6b51aac 100644
--- a/tests/alltypes_proto3_callback/encode_alltypes_callback.c
+++ b/tests/alltypes_proto3_callback/encode_alltypes_callback.c
@@ -13,13 +13,13 @@
 static bool write_varint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
 {
     return pb_encode_tag_for_field(stream, field) &&
-           pb_encode_varint(stream, (long)*arg);
+           pb_encode_varint(stream, (intptr_t)*arg);
 }
 
 static bool write_svarint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
 {
     return pb_encode_tag_for_field(stream, field) &&
-           pb_encode_svarint(stream, (long)*arg);
+           pb_encode_svarint(stream, (intptr_t)*arg);
 }
 
 static bool write_fixed32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
@@ -34,6 +34,18 @@
            pb_encode_fixed64(stream, *arg);
 }
 
+static bool write_double(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+    if (sizeof(double) == sizeof(float))
+        return pb_encode_tag_for_field(stream, field) &&
+               pb_encode_float_as_double(stream, *(float*)*arg);
+#endif
+
+    return pb_encode_tag_for_field(stream, field) &&
+           pb_encode_fixed64(stream, *arg);
+}
+
 static bool write_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
 {
     return pb_encode_tag_for_field(stream, field) &&
@@ -65,7 +77,7 @@
            pb_encode_tag_for_field(stream, field) &&
            pb_encode_varint(stream, 0) &&
            pb_encode_tag_for_field(stream, field) &&
-           pb_encode_varint(stream, (long)*arg);
+           pb_encode_varint(stream, (intptr_t)*arg);
 }
 
 static bool write_repeated_svarint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
@@ -79,7 +91,7 @@
            pb_encode_tag_for_field(stream, field) &&
            pb_encode_svarint(stream, 0) &&
            pb_encode_tag_for_field(stream, field) &&
-           pb_encode_svarint(stream, (long)*arg);
+           pb_encode_svarint(stream, (intptr_t)*arg);
 }
 
 static bool write_repeated_fixed32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
@@ -110,6 +122,31 @@
            pb_encode_fixed64(stream, *arg);
 }
 
+static bool write_repeated_double(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    uint64_t dummy = 0;
+
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+    if (sizeof(double) == sizeof(float))
+        return pb_encode_tag(stream, PB_WT_STRING, field->tag) &&
+               pb_encode_varint(stream, 5 * 8) && /* Number of bytes */
+               pb_encode_float_as_double(stream, 0.0f) &&
+               pb_encode_float_as_double(stream, 0.0f) &&
+               pb_encode_float_as_double(stream, 0.0f) &&
+               pb_encode_float_as_double(stream, 0.0f) &&
+               pb_encode_float_as_double(stream, *(float*)*arg);
+#endif
+
+    /* Make it a packed field */
+    return pb_encode_tag(stream, PB_WT_STRING, field->tag) &&
+           pb_encode_varint(stream, 5 * 8) && /* Number of bytes */
+           pb_encode_fixed64(stream, &dummy) &&
+           pb_encode_fixed64(stream, &dummy) &&
+           pb_encode_fixed64(stream, &dummy) &&
+           pb_encode_fixed64(stream, &dummy) &&
+           pb_encode_fixed64(stream, *arg);
+}
+
 static bool write_repeated_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
 {
     return pb_encode_tag_for_field(stream, field) &&
@@ -235,7 +272,7 @@
     alltypes.rep_sfixed64.funcs.encode = &write_repeated_fixed64;
     alltypes.rep_sfixed64.arg = &rep_sfixed64;
     
-    alltypes.rep_double.funcs.encode = &write_repeated_fixed64;
+    alltypes.rep_double.funcs.encode = &write_repeated_double;
     alltypes.rep_double.arg = &rep_double;
     
     alltypes.rep_string.funcs.encode = &write_repeated_string;
@@ -296,7 +333,7 @@
         alltypes.sng_sfixed64.funcs.encode = &write_fixed64;
         alltypes.sng_sfixed64.arg = &sng_sfixed64;
         
-        alltypes.sng_double.funcs.encode = &write_fixed64;
+        alltypes.sng_double.funcs.encode = &write_double;
         alltypes.sng_double.arg = &sng_double;
         
         alltypes.sng_string.funcs.encode = &write_string;
diff --git a/tests/backwards_compatibility/alltypes_legacy.c b/tests/backwards_compatibility/alltypes_legacy.c
index 1c5d75d..53d0239 100644
--- a/tests/backwards_compatibility/alltypes_legacy.c
+++ b/tests/backwards_compatibility/alltypes_legacy.c
@@ -28,12 +28,13 @@
 
 
 
-
+#ifndef PB_CONVERT_DOUBLE_FLOAT
 /* On some platforms (such as AVR), double is really float.
  * Using double on these platforms is not directly supported
  * by nanopb, but see example_avr_double.
  * To get rid of this error, remove any double fields from your .proto.
  */
 PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES)
+#endif
 
 /* @@protoc_insertion_point(eof) */
diff --git a/tests/common/unittests.h b/tests/common/unittests.h
index c2b470a..6724fed 100644
--- a/tests/common/unittests.h
+++ b/tests/common/unittests.h
@@ -1,5 +1,19 @@
 #include <stdio.h>
 
+#ifdef UNITTESTS_SHORT_MSGS
+/* Short debug messages for platforms with limited memory */
+#define COMMENT(x) printf("\n----" x "----\n");
+#define TEST(x) \
+    if (!(x)) { \
+        fprintf(stderr, "FAIL: Line %d\n", __LINE__); \
+        status = 1; \
+    } else { \
+        printf("OK: Line %d\n", __LINE__); \
+    }
+
+#else
+
+/* Elaborate debug messages for normal development */
 #define COMMENT(x) printf("\n----" x "----\n");
 #define STR(x) #x
 #define STR2(x) STR(x)
@@ -10,5 +24,5 @@
     } else { \
         printf("\033[32;1mOK:\033[22;39m " #x "\n"); \
     }
-
+#endif
 
diff --git a/tests/cxx_descriptor/message_descriptor.cc b/tests/cxx_descriptor/message_descriptor.cc
index cfa6848..4437301 100644
--- a/tests/cxx_descriptor/message_descriptor.cc
+++ b/tests/cxx_descriptor/message_descriptor.cc
@@ -2,7 +2,7 @@
 #include "message.pb.h"
 #include "unittests.h"
 
-int main() {
+extern "C" int main() {
   using namespace nanopb;
 
 #if __cplusplus >= 201103L
diff --git a/tests/encode_unittests/encode_unittests.c b/tests/encode_unittests/encode_unittests.c
index 06188c9..58bd142 100644
--- a/tests/encode_unittests/encode_unittests.c
+++ b/tests/encode_unittests/encode_unittests.c
@@ -214,6 +214,7 @@
         pb_field_iter_t field;
         
         COMMENT("Test pb_enc_fixed using float")
+        field.type = PB_LTYPE_FIXED32;
         field.data_size = sizeof(fvalue);
         field.pData = &fvalue;
         fvalue = 0.0f;
@@ -224,6 +225,7 @@
         TEST(WRITES(pb_enc_fixed(&s, &field), "\x4e\x61\x3c\xcb"))
     
         COMMENT("Test pb_enc_fixed using double")
+        field.type = PB_LTYPE_FIXED64;
         field.data_size = sizeof(dvalue);
         field.pData = &dvalue;
         dvalue = 0.0;
diff --git a/tests/float_double_conversion/SConscript b/tests/float_double_conversion/SConscript
new file mode 100644
index 0000000..21c9a81
--- /dev/null
+++ b/tests/float_double_conversion/SConscript
@@ -0,0 +1,21 @@
+Import("env")
+
+env.NanopbProto("doublemsg")
+
+# Define the compilation options
+opts = env.Clone()
+opts.Append(CPPDEFINES = {'PB_CONVERT_DOUBLE_FLOAT': 1})
+
+if opts.get('EMBEDDED') == 'AVR':
+    opts.Append(CFLAGS = "-Wno-overflow")
+
+# Build new version of core
+strict = opts.Clone()
+strict.Append(CFLAGS = strict['CORECFLAGS'])
+strict.Object("pb_decode_fldbl.o", "$NANOPB/pb_decode.c")
+strict.Object("pb_encode_fldbl.o", "$NANOPB/pb_encode.c")
+strict.Object("pb_common_fldbl.o", "$NANOPB/pb_common.c")
+
+# Build and run test
+test = opts.Program(["float_double_conversion.c", "doublemsg.pb.c", "pb_encode_fldbl.o", "pb_decode_fldbl.o", "pb_common_fldbl.o"])
+env.RunTest(test)
diff --git a/tests/float_double_conversion/doublemsg.proto b/tests/float_double_conversion/doublemsg.proto
new file mode 100644
index 0000000..d022692
--- /dev/null
+++ b/tests/float_double_conversion/doublemsg.proto
@@ -0,0 +1,5 @@
+syntax = "proto2";
+
+message DoubleMsg {
+    required double value = 1;
+}
diff --git a/tests/float_double_conversion/float_double_conversion.c b/tests/float_double_conversion/float_double_conversion.c
new file mode 100644
index 0000000..70a3664
--- /dev/null
+++ b/tests/float_double_conversion/float_double_conversion.c
@@ -0,0 +1,81 @@
+#define _USE_MATH_DEFINES 1
+#undef __STRICT_ANSI__
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#include <pb_decode.h>
+#include <pb_encode.h>
+#include "doublemsg.pb.h"
+#include "unittests.h"
+
+/* This message mimics how DoubleMsg would appear on e.g. AVR. */
+typedef struct {
+    float value;
+} FloatMsg;
+PB_BIND(DoubleMsg, FloatMsg, AUTO)
+
+static const double testvalues[] = {
+           0.0,        -0.0,         0.1,         -0.1,
+          M_PI,       -M_PI,  123456.789,  -123456.789,
+#if defined(NAN) && defined(INFINITY)
+      INFINITY,   -INFINITY,         NAN, INFINITY - INFINITY,
+#endif
+          1e38,       -1e38,        1e39,        -1e39,
+         1e-38,      -1e-38,       1e-39,       -1e-39,
+   3.14159e-37,-3.14159e-37, 3.14159e-43, -3.14159e-43,
+         1e-60,      -1e-60,       1e-45,       -1e-45,
+    0.99999999999999, -0.99999999999999, 127.999999999999, -127.999999999999
+};
+
+#define TESTVALUES_COUNT (sizeof(testvalues)/sizeof(testvalues[0]))
+
+int main()
+{
+    uint8_t buf[16];
+    size_t msglen;
+    int status = 0;
+    int i;
+    for (i = 0; i < TESTVALUES_COUNT; i++)
+    {
+        double orig_double = testvalues[i];
+        float expected_float = (float)orig_double;
+        double expected_double = (double)expected_float;
+
+        printf("\n---- Testcase: %f ----\n", expected_float);
+
+        {
+            /* Encode the original double */
+            pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf));
+            DoubleMsg msg = { 0.0 };
+            msg.value = orig_double;
+            TEST(pb_encode(&stream, &DoubleMsg_msg, &msg));
+            msglen = stream.bytes_written;
+            TEST(msglen == 9);
+        }
+
+        {
+            /* Decode as float */
+            pb_ostream_t ostream;
+            pb_istream_t stream = pb_istream_from_buffer(buf, msglen);
+            FloatMsg msg = { 0.0f };
+            TEST(pb_decode(&stream, &FloatMsg_msg, &msg));
+            TEST(memcmp(&msg.value, &expected_float, sizeof(float)) == 0);
+
+            /* Re-encode */
+            ostream = pb_ostream_from_buffer(buf, sizeof(buf));
+            TEST(pb_encode(&ostream, &FloatMsg_msg, &msg));
+            msglen = ostream.bytes_written;
+            TEST(msglen == 9);
+        }
+
+        {
+            /* Decode as double */
+            pb_istream_t stream = pb_istream_from_buffer(buf, msglen);
+            DoubleMsg msg = { 0.0 };
+            TEST(pb_decode(&stream, &DoubleMsg_msg, &msg));
+            TEST(memcmp(&msg.value, &expected_double, sizeof(double)) == 0);
+        }
+    }
+
+    return status;
+}
diff --git a/tests/fuzztest/SConscript b/tests/fuzztest/SConscript
index 52033e1..7723d99 100644
--- a/tests/fuzztest/SConscript
+++ b/tests/fuzztest/SConscript
@@ -36,14 +36,6 @@
     iterations = 10000
 env.RunTest(fuzz, ARGS = [str(seed), str(iterations)])
 
-fuzzstub = malloc_env.Program(["fuzzstub.c",
-                    "alltypes_pointer.pb.c",
-                    "alltypes_static.pb.c",
-                    "$COMMON/pb_encode_with_malloc.o",
-                    "$COMMON/pb_decode_with_malloc.o",
-                    "$COMMON/pb_common_with_malloc.o",
-                    "$COMMON/malloc_wrappers.o"])
-
 generate_message = malloc_env.Program(["generate_message.c",
                     "alltypes_static.pb.c",
                     "$COMMON/pb_encode.o",
diff --git a/tests/fuzztest/fuzzstub.c b/tests/fuzztest/fuzzstub.c
deleted file mode 100644
index ec9e2af..0000000
--- a/tests/fuzztest/fuzzstub.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/* Fuzz testing for the nanopb core.
- * This can be used with external fuzzers, e.g. radamsa.
- * It performs most of the same checks as fuzztest, but does not feature data generation.
- */
-
-#include <pb_decode.h>
-#include <pb_encode.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <time.h>
-#include <malloc_wrappers.h>
-#include "alltypes_static.pb.h"
-#include "alltypes_pointer.pb.h"
-
-#define BUFSIZE 4096
-
-static bool do_static_decode(uint8_t *buffer, size_t msglen, bool assert_success)
-{
-    pb_istream_t stream;
-    bool status;
-    
-    alltypes_static_AllTypes *msg = malloc_with_check(sizeof(alltypes_static_AllTypes));
-    stream = pb_istream_from_buffer(buffer, msglen);
-    status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg);
-    
-    if (!status && assert_success)
-    {
-        /* Anything that was successfully encoded, should be decodeable.
-         * One exception: strings without null terminator are encoded up
-         * to end of buffer, but refused on decode because the terminator
-         * would not fit. */
-        if (strcmp(stream.errmsg, "string overflow") != 0)
-            assert(status);
-    }
-    
-    free_with_check(msg);
-    return status;
-}
-
-static bool do_pointer_decode(uint8_t *buffer, size_t msglen, bool assert_success)
-{
-    pb_istream_t stream;
-    bool status;
-    alltypes_pointer_AllTypes *msg;
-    
-    msg = malloc_with_check(sizeof(alltypes_pointer_AllTypes));
-    memset(msg, 0, sizeof(alltypes_pointer_AllTypes));
-    stream = pb_istream_from_buffer(buffer, msglen);
-
-    assert(get_alloc_count() == 0);
-    status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg);
-    
-    if (assert_success)
-        assert(status);
-    
-    pb_release(alltypes_pointer_AllTypes_fields, msg);    
-    assert(get_alloc_count() == 0);
-    
-    free_with_check(msg);
-
-    return status;
-}
-
-/* Do a decode -> encode -> decode -> encode roundtrip */
-static void do_static_roundtrip(uint8_t *buffer, size_t msglen)
-{
-    bool status;
-    uint8_t *buf2 = malloc_with_check(BUFSIZE);
-    uint8_t *buf3 = malloc_with_check(BUFSIZE);
-    size_t msglen2, msglen3;
-    alltypes_static_AllTypes *msg1 = malloc_with_check(sizeof(alltypes_static_AllTypes));
-    alltypes_static_AllTypes *msg2 = malloc_with_check(sizeof(alltypes_static_AllTypes));
-    memset(msg1, 0, sizeof(alltypes_static_AllTypes));
-    memset(msg2, 0, sizeof(alltypes_static_AllTypes));
-    
-    {
-        pb_istream_t stream = pb_istream_from_buffer(buffer, msglen);
-        status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg1);
-        assert(status);
-    }
-    
-    {
-        pb_ostream_t stream = pb_ostream_from_buffer(buf2, BUFSIZE);
-        status = pb_encode(&stream, alltypes_static_AllTypes_fields, msg1);
-        assert(status);
-        msglen2 = stream.bytes_written;
-    }
-    
-    {
-        pb_istream_t stream = pb_istream_from_buffer(buf2, msglen2);
-        status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg2);
-        assert(status);
-    }
-    
-    {
-        pb_ostream_t stream = pb_ostream_from_buffer(buf3, BUFSIZE);
-        status = pb_encode(&stream, alltypes_static_AllTypes_fields, msg2);
-        assert(status);
-        msglen3 = stream.bytes_written;
-    }
-    
-    assert(msglen2 == msglen3);
-    assert(memcmp(buf2, buf3, msglen2) == 0);
-    
-    free_with_check(msg1);
-    free_with_check(msg2);
-    free_with_check(buf2);
-    free_with_check(buf3);
-}
-
-/* Do decode -> encode -> decode -> encode roundtrip */
-static void do_pointer_roundtrip(uint8_t *buffer, size_t msglen)
-{
-    bool status;
-    uint8_t *buf2 = malloc_with_check(BUFSIZE);
-    uint8_t *buf3 = malloc_with_check(BUFSIZE);
-    size_t msglen2, msglen3;
-    alltypes_pointer_AllTypes *msg1 = malloc_with_check(sizeof(alltypes_pointer_AllTypes));
-    alltypes_pointer_AllTypes *msg2 = malloc_with_check(sizeof(alltypes_pointer_AllTypes));
-    memset(msg1, 0, sizeof(alltypes_pointer_AllTypes));
-    memset(msg2, 0, sizeof(alltypes_pointer_AllTypes));
-    
-    {
-        pb_istream_t stream = pb_istream_from_buffer(buffer, msglen);
-        status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg1);
-        assert(status);
-    }
-    
-    {
-        pb_ostream_t stream = pb_ostream_from_buffer(buf2, BUFSIZE);
-        status = pb_encode(&stream, alltypes_pointer_AllTypes_fields, msg1);
-        assert(status);
-        msglen2 = stream.bytes_written;
-    }
-    
-    {
-        pb_istream_t stream = pb_istream_from_buffer(buf2, msglen2);
-        status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg2);
-        assert(status);
-    }
-    
-    {
-        pb_ostream_t stream = pb_ostream_from_buffer(buf3, BUFSIZE);
-        status = pb_encode(&stream, alltypes_pointer_AllTypes_fields, msg2);
-        assert(status);
-        msglen3 = stream.bytes_written;
-    }
-    
-    assert(msglen2 == msglen3);
-    assert(memcmp(buf2, buf3, msglen2) == 0);
-    
-    pb_release(alltypes_pointer_AllTypes_fields, msg1);
-    pb_release(alltypes_pointer_AllTypes_fields, msg2);
-    free_with_check(msg1);
-    free_with_check(msg2);
-    free_with_check(buf2);
-    free_with_check(buf3);
-}
-
-static void run_iteration()
-{
-    uint8_t *buffer = malloc_with_check(BUFSIZE);
-    size_t msglen;
-    bool status;
-    
-    msglen = fread(buffer, 1, BUFSIZE, stdin);
-
-    status = do_static_decode(buffer, msglen, false);
-    
-    if (status)
-        do_static_roundtrip(buffer, msglen);
-    
-    status = do_pointer_decode(buffer, msglen, false);
-    
-    if (status)
-        do_pointer_roundtrip(buffer, msglen);
-    
-    free_with_check(buffer);
-}
-
-int main(int argc, char **argv)
-{
-    run_iteration();
-    
-    return 0;
-}
-
diff --git a/tests/fuzztest/fuzztest.c b/tests/fuzztest/fuzztest.c
index 7e9ddb7..93065ae 100644
--- a/tests/fuzztest/fuzztest.c
+++ b/tests/fuzztest/fuzztest.c
@@ -8,11 +8,17 @@
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
-#include <time.h>
 #include <malloc_wrappers.h>
+#include "test_helpers.h"
 #include "alltypes_static.pb.h"
 #include "alltypes_pointer.pb.h"
 
+#ifdef __AVR__
+#define BUFSIZE 2048
+#else
+#define BUFSIZE 4096
+#endif
+
 static uint64_t random_seed;
 
 /* Uses xorshift64 here instead of rand() for both speed and
@@ -198,8 +204,6 @@
     }
 }
 
-#define BUFSIZE 4096
-
 static bool do_static_encode(uint8_t *buffer, size_t *msglen)
 {
     pb_ostream_t stream;
@@ -444,19 +448,49 @@
     assert(get_alloc_count() == 0);
 }
 
+static void run_stub()
+{
+    uint8_t *buffer = malloc_with_check(BUFSIZE);
+    size_t msglen;
+    bool status;
+
+    SET_BINARY_MODE(stdin);
+    msglen = fread(buffer, 1, BUFSIZE, stdin);
+
+    status = do_static_decode(buffer, msglen, false);
+
+    if (status)
+        do_static_roundtrip(buffer, msglen);
+
+    status = do_pointer_decode(buffer, msglen, false);
+
+    if (status)
+        do_pointer_roundtrip(buffer, msglen);
+
+    free_with_check(buffer);
+}
+
 int main(int argc, char **argv)
 {
     int i;
     int iterations;
 
-    random_seed = atol(argv[1]);
-    iterations = atol(argv[2]);
-    if (iterations == 0) iterations = 10000;
-    fprintf(stderr, "Random seed: %u, iterations: %d\n", (unsigned)random_seed, iterations);
-    
-    for (i = 0; i < iterations; i++)
+    if (argc >= 2)
     {
-        run_iteration();
+        /* Run in stand-alone mode */
+        random_seed = atol(argv[1]);
+        iterations = (argc >= 3) ? atol(argv[2]) : 10000;
+        fprintf(stderr, "Random seed: %u, iterations: %d\n", (unsigned)random_seed, iterations);
+
+        for (i = 0; i < iterations; i++)
+        {
+            run_iteration();
+        }
+    }
+    else
+    {
+        /* Run as a stub for afl-fuzz and similar */
+        run_stub();
     }
     
     return 0;
diff --git a/tests/fuzztest/generate_message.c b/tests/fuzztest/generate_message.c
index 73959fe..b77068a 100644
--- a/tests/fuzztest/generate_message.c
+++ b/tests/fuzztest/generate_message.c
@@ -66,7 +66,7 @@
 static void generate_message()
 {
     alltypes_static_AllTypes msg;
-    uint8_t buf[8192];
+    uint8_t buf[4096];
     pb_ostream_t stream = {0};
     
     do {
@@ -83,16 +83,15 @@
 
 int main(int argc, char **argv)
 {
-    if (argc > 1)
+    if (argc < 2)
     {
-        random_seed = atol(argv[1]);
+        fprintf(stderr, "Usage: generate_message <seed>\n");
+        return 1;
     }
-    else
-    {
-        random_seed = time(NULL);
-    }
-    
-    fprintf(stderr, "Random seed: %llu\n", (long long unsigned)random_seed);
+
+    random_seed = atol(argv[1]);
+
+    fprintf(stderr, "Random seed: %u\n", (unsigned)random_seed);
     
     generate_message();
     
diff --git a/tests/fuzztest/run_radamsa.sh b/tests/fuzztest/run_radamsa.sh
deleted file mode 100755
index 52cd40a..0000000
--- a/tests/fuzztest/run_radamsa.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-
-TMP=`tempfile`
-
-echo $TMP
-while true
-do
-   radamsa sample_data/* > $TMP
-   $1 < $TMP
-   test $? -gt 127 && break
-done
- 
diff --git a/tests/msgid/SConscript b/tests/msgid/SConscript
new file mode 100644
index 0000000..549a569
--- /dev/null
+++ b/tests/msgid/SConscript
@@ -0,0 +1,22 @@
+# Test the 'msgid' feature for identifying encoded messages
+
+Import('env')
+
+env.NanopbProto('msgid_example')
+
+enc = env.Program(['encode_msgid.c',
+                'msgid_example.pb.c',
+                '$COMMON/pb_encode.o',
+                '$COMMON/pb_common.o'])
+
+dec = env.Program(['decode_msgid.c',
+                'msgid_example.pb.c',
+                '$COMMON/pb_decode.o',
+                '$COMMON/pb_common.o'])
+
+env.RunTest("message1.pb", enc, ARGS = ['1'])
+env.RunTest("message1.txt", [dec, 'message1.pb'])
+env.RunTest("message2.pb", enc, ARGS = ['2'])
+env.RunTest("message2.txt", [dec, 'message2.pb'])
+env.RunTest("message3.pb", enc, ARGS = ['3'])
+env.RunTest("message3.txt", [dec, 'message3.pb'])
diff --git a/tests/msgid/decode_msgid.c b/tests/msgid/decode_msgid.c
new file mode 100644
index 0000000..2623f8c
--- /dev/null
+++ b/tests/msgid/decode_msgid.c
@@ -0,0 +1,105 @@
+/* Decode a message using msgid prefix to identify message type */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pb_decode.h>
+#include "msgid_example.pb.h"
+#include "test_helpers.h"
+
+/* This function reads the prefix written by sending side. */
+bool read_prefix(pb_istream_t *stream, int *msgid)
+{
+    uint8_t prefix = 0;
+
+    if (!pb_read(stream, &prefix, 1))
+        return false;
+
+    *msgid = prefix;
+    return true;
+}
+
+/* Main function will call one of these functions based on the prefix */
+
+bool handle_MyMessage1(pb_istream_t *stream)
+{
+    MyMessage1 msg = MyMessage1_init_default;
+
+    if (!pb_decode(stream, MyMessage1_fields, &msg))
+        return false;
+
+    printf("Got MyMessage1: intvalue = %d\n", (int)msg.intvalue);
+    return true;
+}
+
+bool handle_MyMessage2(pb_istream_t *stream)
+{
+    MyMessage2 msg = MyMessage2_init_default;
+
+    if (!pb_decode(stream, MyMessage2_fields, &msg))
+        return false;
+
+    printf("Got MyMessage2: intvalue = %d, strvalue = %s\n",
+           (int)msg.intvalue, msg.strvalue);
+    return true;
+}
+
+bool handle_MyMessage3(pb_istream_t *stream)
+{
+    MyMessage3 msg = MyMessage3_init_default;
+
+    if (!pb_decode(stream, MyMessage3_fields, &msg))
+        return false;
+
+    printf("Got MyMessage3: boolvalue = %d\n", (int)msg.boolvalue);
+    return true;
+}
+
+int main(int argc, char **argv)
+{
+    uint8_t buffer[128];
+    pb_istream_t stream;
+    size_t count;
+    bool status = false;
+    int prefix;
+
+    /* Read the data into buffer */
+    SET_BINARY_MODE(stdin);
+    count = fread(buffer, 1, sizeof(buffer), stdin);
+
+    if (!feof(stdin))
+    {
+        printf("Message does not fit in buffer\n");
+        return 1;
+    }
+
+    stream = pb_istream_from_buffer(buffer, count);
+
+    if (!read_prefix(&stream, &prefix))
+    {
+        printf("Failed to read prefix: %s\n", PB_GET_ERROR(&stream));
+        return 1;
+    }
+
+    /* Call message handler based on prefix.
+     * We could write the switch cases manually, comparing against
+     * the MyMessageX_msgid defines. However, this uses the automatically
+     * generated X-macro construct to make the switch case.
+     */
+
+    switch (prefix)
+    {
+        #define PB_MSG(id,len,name) case id: status = handle_ ## name(&stream); break;
+        MSGID_EXAMPLE_MESSAGES
+        #undef PB_MSG
+
+        default: printf("Unknown prefix: %d\n", prefix); return 1;
+    }
+
+    if (!status)
+    {
+        printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
+        return 1;
+    } else {
+        return 0;
+    }
+}
diff --git a/tests/msgid/encode_msgid.c b/tests/msgid/encode_msgid.c
new file mode 100644
index 0000000..4fa1279
--- /dev/null
+++ b/tests/msgid/encode_msgid.c
@@ -0,0 +1,90 @@
+/* Encode a message using msgid field as prefix */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pb_encode.h>
+#include "msgid_example.pb.h"
+#include "test_helpers.h"
+
+/* This function writes the message id as a prefix to the message, allowing
+ * the receiving side to identify message type. Here we use uint8_t to store
+ * it, but e.g. varint or some custom header struct would work just as well.
+ */
+bool write_prefix(pb_ostream_t *stream, int msgid)
+{
+    uint8_t prefix = msgid;
+    return pb_write(stream, &prefix, 1);
+}
+
+/* The main logic will call one of these functions.
+ * Normally which function you call would be selected based on what message
+ * you want to send, here it is decided based on command line parameter.
+ */
+
+bool encode_MyMessage1(pb_ostream_t *stream)
+{
+    MyMessage1 msg = MyMessage1_init_default;
+    msg.intvalue = 1234;
+    return write_prefix(stream, MyMessage1_msgid)
+           && pb_encode(stream, MyMessage1_fields, &msg);
+}
+
+bool encode_MyMessage2(pb_ostream_t *stream)
+{
+    MyMessage2 msg = MyMessage2_init_default;
+    msg.intvalue = 9999;
+    strcpy(msg.strvalue, "Msg2");
+    return write_prefix(stream, MyMessage2_msgid)
+           && pb_encode(stream, MyMessage2_fields, &msg);
+}
+
+bool encode_MyMessage3(pb_ostream_t *stream)
+{
+    MyMessage3 msg = MyMessage3_init_default;
+    msg.boolvalue = true;
+    return write_prefix(stream, MyMessage3_msgid)
+           && pb_encode(stream, MyMessage3_fields, &msg);
+}
+
+int main(int argc, char **argv)
+{
+    uint8_t buffer[128];
+    pb_ostream_t stream;
+    bool status = false;
+    int option;
+
+    if (argc != 2)
+    {
+        fprintf(stderr, "Usage: encode_msgid [number]\n");
+        return 1;
+    }
+    option = atoi(argv[1]);
+
+    stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+
+    if (option == 1)
+    {
+        status = encode_MyMessage1(&stream);
+    }
+    else if (option == 2)
+    {
+        status = encode_MyMessage2(&stream);
+    }
+    else if (option == 3)
+    {
+        status = encode_MyMessage3(&stream);
+    }
+
+    if (status)
+    {
+        SET_BINARY_MODE(stdout);
+        fwrite(buffer, 1, stream.bytes_written, stdout);
+        return 0;
+    }
+    else
+    {
+        fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream));
+        return 1;
+    }
+}
diff --git a/tests/msgid/msgid_example.proto b/tests/msgid/msgid_example.proto
new file mode 100644
index 0000000..137292f
--- /dev/null
+++ b/tests/msgid/msgid_example.proto
@@ -0,0 +1,25 @@
+syntax = "proto3";
+
+import "nanopb.proto";
+
+message MyMessage1
+{
+    option (nanopb_msgopt).msgid = 1;
+
+    int32 intvalue = 1;
+}
+
+message MyMessage2
+{
+    option (nanopb_msgopt).msgid = 2;
+
+    int32 intvalue = 1;
+    string strvalue = 2 [(nanopb).max_size = 16];
+}
+
+message MyMessage3
+{
+    option (nanopb_msgopt).msgid = 3;
+
+    bool boolvalue = 1;
+}
diff --git a/tests/site_scons/platforms/avr/__init__.py b/tests/site_scons/platforms/avr/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/site_scons/platforms/avr/__init__.py
diff --git a/tests/site_scons/platforms/avr/avr.py b/tests/site_scons/platforms/avr/avr.py
new file mode 100644
index 0000000..83d3249
--- /dev/null
+++ b/tests/site_scons/platforms/avr/avr.py
@@ -0,0 +1,23 @@
+# Compiler settings for running the tests on a simulated atmega1284.
+
+def set_avr_platform(env):
+    native = env.Clone()
+    native.Append(LIBS = ["simavr", "libelf"], CFLAGS = "-Wall -Werror -g")
+    runner = native.Program("build/run_test", "site_scons/platforms/avr/run_test.c")
+    
+    env.Replace(EMBEDDED = "AVR")
+    env.Replace(CC  = "avr-gcc",
+                CXX = "avr-g++",
+                LD  = "avr-gcc")
+    env.Replace(TEST_RUNNER = "build/run_test")
+    env.Append(CFLAGS = "-mmcu=atmega1284 -Dmain=app_main -Os")
+    env.Append(CXXFLAGS = "-mmcu=atmega1284 -Dmain=app_main -Os")
+    env.Append(CPPDEFINES = {'PB_CONVERT_DOUBLE_FLOAT': 1, 'UNITTESTS_SHORT_MSGS': 1})
+    env.Append(LINKFLAGS = "-mmcu=atmega1284")
+    env.Append(LINKFLAGS = "build/avr_io.o -Wl,-Map,avr.map")
+    avr_io = env.Object("build/avr_io.o", "site_scons/platforms/avr/avr_io.c")
+    
+    # These fake defines just ensure that the needed platform files get built.
+    env.Depends("build/common/pb_common.o", runner)
+    env.Depends("build/common/pb_common.o", avr_io)
+    
diff --git a/tests/site_scons/platforms/avr/avr_io.c b/tests/site_scons/platforms/avr/avr_io.c
new file mode 100644
index 0000000..eff00da
--- /dev/null
+++ b/tests/site_scons/platforms/avr/avr_io.c
@@ -0,0 +1,58 @@
+/* This wrapper file initializes stdio to UART connection and
+ * receives the list of command line arguments.
+ */
+#include <stdint.h>
+#include <stdio.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/sleep.h>
+
+#undef main
+extern int app_main(int argc, const char **argv);
+
+struct {
+    uint8_t argc;
+    char args[3][16];
+} g_args;
+
+int uart_putchar(char c, FILE *stream)
+{
+    loop_until_bit_is_set(UCSR0A, UDRE0);
+    UDR0 = c;
+    return 0;
+}
+
+int uart_getchar(FILE *stream)
+{
+    while (bit_is_clear(UCSR0A, RXC0) && bit_is_clear(UCSR0A, FE0));
+    if (UCSR0A & _BV(FE0)) return _FDEV_EOF; /* Break = EOF */
+    return UDR0;
+}
+
+FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
+
+int main(void)
+{
+    const char *argv[4] = {"main", g_args.args[0], g_args.args[1], g_args.args[2]};
+    int status;
+    
+    UBRR0 = (8000000 / (16UL * 9600)) - 1; /* 9600 bps with default 8 MHz clock */
+    UCSR0B = _BV(TXEN0) | _BV(RXEN0);
+    
+    stdout = stdin = stderr = &uart_str;
+    
+    fread((char*)&g_args, 1, sizeof(g_args), stdin);
+    
+    status = app_main(g_args.argc + 1, argv);
+
+    DDRB = 3;
+    if (status)
+        PORTB = 1;
+    else
+        PORTB = 2;
+  
+    cli();
+    sleep_mode();
+    return status;
+}
+
diff --git a/tests/site_scons/platforms/avr/run_test.c b/tests/site_scons/platforms/avr/run_test.c
new file mode 100644
index 0000000..35cfa55
--- /dev/null
+++ b/tests/site_scons/platforms/avr/run_test.c
@@ -0,0 +1,195 @@
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <libgen.h>
+#include <simavr/sim_avr.h>
+#include <simavr/sim_gdb.h>
+#include <simavr/avr_ioport.h>
+#include <simavr/sim_elf.h>
+#include <simavr/avr_uart.h>
+
+static avr_t *g_avr;
+static avr_irq_t *g_uart_irq;
+
+static struct {
+    uint8_t argc;
+    char args[3][16];
+} g_args;
+static int g_args_idx;
+static bool g_uart_xon;
+static bool g_status_ok;
+
+static void uart_tx_hook(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+    fputc(value, stdout);
+    fflush(stdout);
+}
+
+static bool stdin_can_read()  
+{
+  struct timeval tv;
+  fd_set fds;
+  tv.tv_sec = 0;
+  tv.tv_usec = 0;
+  FD_ZERO(&fds);
+  FD_SET(STDIN_FILENO, &fds);
+  select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
+  return (FD_ISSET(0, &fds));
+}
+
+static void avr_logger(avr_t * avr, const int level, const char * format, va_list ap)
+{
+    if ((!avr && level <= LOG_WARNING) || (avr && avr->log >= level)) {
+        vfprintf(stderr , format, ap);
+    }
+}
+
+static void uart_xon_hook(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+    g_uart_xon = true;
+    int v;
+    
+    if (feof(stdin))
+    {
+        avr_raise_irq(&g_uart_irq[1], UART_INPUT_FE);
+        return;
+    }
+
+    while (g_uart_xon)
+    {
+        if (g_args_idx < sizeof(g_args))
+        {
+            v = ((char*)&g_args)[g_args_idx++];
+        }
+        else if (stdin_can_read())
+        {
+            v = fgetc(stdin);
+        }
+        else
+        {
+            break;        
+        }
+        
+        if (v != EOF)
+        {
+            avr_raise_irq(&g_uart_irq[1], v);
+        }
+        else
+        {
+            avr_raise_irq(&g_uart_irq[1], UART_INPUT_FE);
+            break;
+        }
+    }
+}
+
+static void uart_xoff_hook(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+    g_uart_xon = false;
+}
+
+void init_uart()
+{
+    const char *irq_names[2] = {"8<uart_in", "8>uart_out"};
+    g_uart_irq = avr_alloc_irq(&g_avr->irq_pool, 0, 2, irq_names);
+    avr_irq_register_notify(&g_uart_irq[0], &uart_tx_hook, NULL);
+    
+    uint32_t flags = 0;
+    avr_ioctl(g_avr, AVR_IOCTL_UART_GET_FLAGS('0'), &flags);
+    flags &= ~AVR_UART_FLAG_STDIO;
+    avr_ioctl(g_avr, AVR_IOCTL_UART_SET_FLAGS('0'), &flags);
+
+    avr_irq_t *src = avr_io_getirq(g_avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_OUTPUT);
+    avr_irq_t *dst = avr_io_getirq(g_avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_INPUT);
+    avr_connect_irq(src, &g_uart_irq[0]);
+    avr_connect_irq(&g_uart_irq[1], dst);
+    
+    avr_irq_t *xon = avr_io_getirq(g_avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_OUT_XON);
+    avr_irq_t *xoff = avr_io_getirq(g_avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_OUT_XOFF);
+    avr_irq_register_notify(xon, uart_xon_hook, NULL);
+    avr_irq_register_notify(xoff, uart_xoff_hook, NULL);
+}
+
+static void status_ok_hook(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+    g_status_ok = value;
+}
+
+int main(int argc, char *argv[])
+{
+    avr_global_logger_set(&avr_logger);
+    g_avr = avr_make_mcu_by_name("atmega1284");
+    if (!g_avr)
+    {
+        fprintf(stderr, "avr_make_mcu_by_name failed\n");
+        return 1;
+    }
+    
+    if (argc < 2)
+    {
+        fprintf(stderr, "Usage: %s [-g] binary [args ...]\n", argv[0]);
+        return 2;
+    }
+    
+    const char *filename = argv[1];
+    bool enable_gdb = false;
+    int argc_offset = 2;
+    
+    if (strcmp(filename, "-g") == 0)
+    {
+        enable_gdb = true;
+        argc_offset = 3;
+        filename = argv[2];
+    }
+    
+    elf_firmware_t firmware;
+    elf_read_firmware(filename, &firmware);
+    avr_init(g_avr);
+	avr_load_firmware(g_avr, &firmware);
+	g_avr->frequency = 8000000;
+
+    if (enable_gdb)
+    {
+        g_avr->state = cpu_Stopped;
+        g_avr->gdb_port = 1234;
+        avr_gdb_init(g_avr);
+    }
+
+    init_uart();
+    
+    avr_irq_register_notify(avr_io_getirq(g_avr, AVR_IOCTL_IOPORT_GETIRQ('B'), 1), status_ok_hook, NULL);
+
+    // Pass the rest of arguments to application inside simulator
+    g_args.argc = argc - argc_offset;
+    if (g_args.argc > 3) g_args.argc = 3;
+    for (int i = 0; i < g_args.argc; i++)
+    {
+        strncpy(g_args.args[i], argv[i + argc_offset], 15);
+    }
+    
+    while (1)
+    {
+		int state = avr_run(g_avr);
+		if (state == cpu_Done)
+		    break;
+		    
+   		if (state == cpu_Crashed)
+   		{
+   		    fprintf(stderr, "CPU Crashed\n");
+			return 3;
+		}
+	}
+    
+    if (g_status_ok)
+    {
+        return 0;
+    }
+    else
+    {
+        fprintf(stderr, "Received error status from simulation\n");
+        return 5;    
+    }
+}
diff --git a/tests/site_scons/platforms/stm32/stm32.py b/tests/site_scons/platforms/stm32/stm32.py
index 185dd29..2f2e956 100644
--- a/tests/site_scons/platforms/stm32/stm32.py
+++ b/tests/site_scons/platforms/stm32/stm32.py
@@ -4,13 +4,13 @@
 # code is run from RAM also.
 
 def set_stm32_platform(env):
-    env.Replace(EMBEDDED = "1")
+    env.Replace(EMBEDDED = "STM32")
     env.Replace(CC  = "arm-none-eabi-gcc",
                 CXX = "arm-none-eabi-g++",
                 LD  = "arm-none-eabi-gcc")
     env.Replace(TEST_RUNNER = "site_scons/platforms/stm32/run_test.sh")
-    env.Append(CFLAGS = "-mcpu=cortex-m3 -mthumb")
-    env.Append(CXXFLAGS = "-mcpu=cortex-m3 -mthumb")
+    env.Append(CFLAGS = "-mcpu=cortex-m3 -mthumb -Os")
+    env.Append(CXXFLAGS = "-mcpu=cortex-m3 -mthumb -Os")
     env.Append(LINKFLAGS = "-mcpu=cortex-m3 -mthumb")
     env.Append(LINKFLAGS = "site_scons/platforms/stm32/vectors.c")
     env.Append(LINKFLAGS = "--specs=rdimon.specs --specs=nano.specs")
diff --git a/tests/site_scons/site_init.py b/tests/site_scons/site_init.py
index cbdf92a..0e485ac 100644
--- a/tests/site_scons/site_init.py
+++ b/tests/site_scons/site_init.py
@@ -2,6 +2,7 @@
 import sys
 import re
 from platforms.stm32.stm32 import set_stm32_platform
+from platforms.avr.avr import set_avr_platform
 
 try:
     # Make terminal colors work on windows