aboutsummaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
authorPetteri Aimonen <jpa@git.mail.kapsi.fi>2013-09-13 12:59:31 +0300
committerPetteri Aimonen <jpa@git.mail.kapsi.fi>2013-09-13 12:59:31 +0300
commitf47410ea4b8ae43e19facd378be4cf1073e1813b (patch)
tree53fdc4bac6615f07e830d7d538c90abbea1ead4b /examples
parentfd9a79a06db00c6199a5dcaee22ed2cd8e3c3e9b (diff)
Move examples into subfolders, add READMEs
Diffstat (limited to 'examples')
-rw-r--r--examples/network_server/Makefile19
-rw-r--r--examples/network_server/README60
-rw-r--r--examples/network_server/client.c116
-rw-r--r--examples/network_server/common.c40
-rw-r--r--examples/network_server/common.h9
-rw-r--r--examples/network_server/fileproto.options13
-rw-r--r--examples/network_server/fileproto.proto18
-rw-r--r--examples/network_server/server.c131
-rw-r--r--examples/using_double_on_avr/Makefile29
-rw-r--r--examples/using_double_on_avr/README25
-rw-r--r--examples/using_double_on_avr/decode_double.c33
-rw-r--r--examples/using_double_on_avr/double_conversion.c123
-rw-r--r--examples/using_double_on_avr/double_conversion.h26
-rw-r--r--examples/using_double_on_avr/doubleproto.proto13
-rw-r--r--examples/using_double_on_avr/encode_double.c25
-rw-r--r--examples/using_double_on_avr/test_conversions.c56
-rw-r--r--examples/using_union_messages/Makefile22
-rw-r--r--examples/using_union_messages/README52
-rw-r--r--examples/using_union_messages/decode.c96
-rw-r--r--examples/using_union_messages/encode.c85
-rw-r--r--examples/using_union_messages/unionproto.proto30
21 files changed, 1021 insertions, 0 deletions
diff --git a/examples/network_server/Makefile b/examples/network_server/Makefile
new file mode 100644
index 00000000..981f2cf9
--- /dev/null
+++ b/examples/network_server/Makefile
@@ -0,0 +1,19 @@
+CFLAGS = -ansi -Wall -Werror -g -O0
+
+# Path to the nanopb root folder
+NANOPB_DIR = ../..
+DEPS = $(NANOPB_DIR)/pb_decode.c $(NANOPB_DIR)/pb_decode.h \
+ $(NANOPB_DIR)/pb_encode.c $(NANOPB_DIR)/pb_encode.h $(NANOPB_DIR)/pb.h
+CFLAGS += -I$(NANOPB_DIR)
+
+all: server client
+
+clean:
+ rm -f server client fileproto.pb.c fileproto.pb.h
+
+%: %.c $(DEPS) fileproto.pb.h fileproto.pb.c
+ $(CC) $(CFLAGS) -o $@ $< $(NANOPB_DIR)/pb_decode.c $(NANOPB_DIR)/pb_encode.c fileproto.pb.c common.c
+
+fileproto.pb.c fileproto.pb.h: fileproto.proto $(NANOPB_DIR)/generator/nanopb_generator.py
+ protoc -ofileproto.pb $<
+ python $(NANOPB_DIR)/generator/nanopb_generator.py fileproto.pb
diff --git a/examples/network_server/README b/examples/network_server/README
new file mode 100644
index 00000000..7bdcbed5
--- /dev/null
+++ b/examples/network_server/README
@@ -0,0 +1,60 @@
+Nanopb example "network_server"
+===============================
+
+This example demonstrates the use of nanopb to communicate over network
+connections. It consists of a server that sends file listings, and of
+a client that requests the file list from the server.
+
+Example usage
+-------------
+
+user@host:~/nanopb/examples/network_server$ make # Build the example
+protoc -ofileproto.pb fileproto.proto
+python ../../generator/nanopb_generator.py fileproto.pb
+Writing to fileproto.pb.h and fileproto.pb.c
+cc -ansi -Wall -Werror -I .. -g -O0 -I../.. -o server server.c
+ ../../pb_decode.c ../../pb_encode.c fileproto.pb.c common.c
+cc -ansi -Wall -Werror -I .. -g -O0 -I../.. -o client client.c
+ ../../pb_decode.c ../../pb_encode.c fileproto.pb.c common.c
+
+user@host:~/nanopb/examples/network_server$ ./server & # Start the server on background
+[1] 24462
+
+petteri@oddish:~/nanopb/examples/network_server$ ./client /bin # Request the server to list /bin
+Got connection.
+Listing directory: /bin
+1327119 bzdiff
+1327126 bzless
+1327147 ps
+1327178 ntfsmove
+1327271 mv
+1327187 mount
+1327259 false
+1327266 tempfile
+1327285 zfgrep
+1327165 gzexe
+1327204 nc.openbsd
+1327260 uname
+
+
+Details of implementation
+-------------------------
+fileproto.proto contains the portable Google Protocol Buffers protocol definition.
+It could be used as-is to implement a server or a client in any other language, for
+example Python or Java.
+
+fileproto.options contains the nanopb-specific options for the protocol file. This
+sets the amount of space allocated for file names when decoding messages.
+
+common.c/h contains functions that allow nanopb to read and write directly from
+network socket. This way there is no need to allocate a separate buffer to store
+the message.
+
+server.c contains the code to open a listening socket, to respond to clients and
+to list directory contents.
+
+client.c contains the code to connect to a server, to send a request and to print
+the response message.
+
+The code is implemented using the POSIX socket api, but it should be easy enough
+to port into any other socket api, such as lwip.
diff --git a/examples/network_server/client.c b/examples/network_server/client.c
new file mode 100644
index 00000000..e6e9a2e0
--- /dev/null
+++ b/examples/network_server/client.c
@@ -0,0 +1,116 @@
+/* This is a simple TCP client that connects to port 1234 and prints a list
+ * of files in a given directory.
+ *
+ * It directly deserializes and serializes messages from network, minimizing
+ * memory use.
+ *
+ * For flexibility, this example is implemented using posix api.
+ * In a real embedded system you would typically use some other kind of
+ * a communication and filesystem layer.
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pb_encode.h>
+#include <pb_decode.h>
+
+#include "fileproto.pb.h"
+#include "common.h"
+
+bool printfile_callback(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+ FileInfo fileinfo;
+
+ if (!pb_decode(stream, FileInfo_fields, &fileinfo))
+ return false;
+
+ printf("%-10lld %s\n", (long long)fileinfo.inode, fileinfo.name);
+
+ return true;
+}
+
+bool listdir(int fd, char *path)
+{
+ ListFilesRequest request;
+ ListFilesResponse response;
+ pb_istream_t input = pb_istream_from_socket(fd);
+ pb_ostream_t output = pb_ostream_from_socket(fd);
+ uint8_t zero = 0;
+
+ if (path == NULL)
+ {
+ request.has_path = false;
+ }
+ else
+ {
+ request.has_path = true;
+ if (strlen(path) + 1 > sizeof(request.path))
+ {
+ fprintf(stderr, "Too long path.\n");
+ return false;
+ }
+
+ strcpy(request.path, path);
+ }
+
+ if (!pb_encode(&output, ListFilesRequest_fields, &request))
+ {
+ fprintf(stderr, "Encoding failed.\n");
+ return false;
+ }
+
+ /* We signal the end of request with a 0 tag. */
+ pb_write(&output, &zero, 1);
+
+ response.file.funcs.decode = &printfile_callback;
+
+ if (!pb_decode(&input, ListFilesResponse_fields, &response))
+ {
+ fprintf(stderr, "Decode failed: %s\n", PB_GET_ERROR(&input));
+ return false;
+ }
+
+ if (response.path_error)
+ {
+ fprintf(stderr, "Server reported error.\n");
+ return false;
+ }
+
+ return true;
+}
+
+int main(int argc, char **argv)
+{
+ int sockfd;
+ struct sockaddr_in servaddr;
+ char *path = NULL;
+
+ if (argc > 1)
+ path = argv[1];
+
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+
+ memset(&servaddr, 0, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ servaddr.sin_port = htons(1234);
+
+ if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0)
+ {
+ perror("connect");
+ return 1;
+ }
+
+ if (!listdir(sockfd, path))
+ return 2;
+
+ close(sockfd);
+
+ return 0;
+}
diff --git a/examples/network_server/common.c b/examples/network_server/common.c
new file mode 100644
index 00000000..04a5aa85
--- /dev/null
+++ b/examples/network_server/common.c
@@ -0,0 +1,40 @@
+/* Simple binding of nanopb streams to TCP sockets.
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <pb_encode.h>
+#include <pb_decode.h>
+
+#include "common.h"
+
+static bool write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count)
+{
+ int fd = (intptr_t)stream->state;
+ return send(fd, buf, count, 0) == count;
+}
+
+static bool read_callback(pb_istream_t *stream, uint8_t *buf, size_t count)
+{
+ int fd = (intptr_t)stream->state;
+ int result;
+
+ result = recv(fd, buf, count, MSG_WAITALL);
+
+ if (result == 0)
+ stream->bytes_left = 0; /* EOF */
+
+ return result == count;
+}
+
+pb_ostream_t pb_ostream_from_socket(int fd)
+{
+ pb_ostream_t stream = {&write_callback, (void*)(intptr_t)fd, SIZE_MAX, 0};
+ return stream;
+}
+
+pb_istream_t pb_istream_from_socket(int fd)
+{
+ pb_istream_t stream = {&read_callback, (void*)(intptr_t)fd, SIZE_MAX};
+ return stream;
+}
diff --git a/examples/network_server/common.h b/examples/network_server/common.h
new file mode 100644
index 00000000..8dab3b7c
--- /dev/null
+++ b/examples/network_server/common.h
@@ -0,0 +1,9 @@
+#ifndef _PB_EXAMPLE_COMMON_H_
+#define _PB_EXAMPLE_COMMON_H_
+
+#include <pb.h>
+
+pb_ostream_t pb_ostream_from_socket(int fd);
+pb_istream_t pb_istream_from_socket(int fd);
+
+#endif \ No newline at end of file
diff --git a/examples/network_server/fileproto.options b/examples/network_server/fileproto.options
new file mode 100644
index 00000000..29a2ab0e
--- /dev/null
+++ b/examples/network_server/fileproto.options
@@ -0,0 +1,13 @@
+# This file defines the nanopb-specific options for the messages defined
+# in fileproto.proto.
+#
+# If you come from high-level programming background, the hardcoded
+# maximum lengths may disgust you. However, if your microcontroller only
+# has a few kB of ram to begin with, setting reasonable limits for
+# filenames is ok.
+#
+# On the other hand, using the callback interface, it is not necessary
+# to set a limit on the number of files in the response.
+
+ListFilesRequest.path max_size:128
+FileInfo.name max_size:128
diff --git a/examples/network_server/fileproto.proto b/examples/network_server/fileproto.proto
new file mode 100644
index 00000000..3e70c492
--- /dev/null
+++ b/examples/network_server/fileproto.proto
@@ -0,0 +1,18 @@
+// This defines protocol for a simple server that lists files.
+//
+// See also the nanopb-specific options in fileproto.options.
+
+message ListFilesRequest {
+ optional string path = 1 [default = "/"];
+}
+
+message FileInfo {
+ required uint64 inode = 1;
+ required string name = 2;
+}
+
+message ListFilesResponse {
+ optional bool path_error = 1 [default = false];
+ repeated FileInfo file = 2;
+}
+
diff --git a/examples/network_server/server.c b/examples/network_server/server.c
new file mode 100644
index 00000000..9a9c2644
--- /dev/null
+++ b/examples/network_server/server.c
@@ -0,0 +1,131 @@
+/* This is a simple TCP server that listens on port 1234 and provides lists
+ * of files to clients, using a protocol defined in file_server.proto.
+ *
+ * It directly deserializes and serializes messages from network, minimizing
+ * memory use.
+ *
+ * For flexibility, this example is implemented using posix api.
+ * In a real embedded system you would typically use some other kind of
+ * a communication and filesystem layer.
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pb_encode.h>
+#include <pb_decode.h>
+
+#include "fileproto.pb.h"
+#include "common.h"
+
+bool listdir_callback(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+ DIR *dir = (DIR*) *arg;
+ struct dirent *file;
+ FileInfo fileinfo;
+
+ while ((file = readdir(dir)) != NULL)
+ {
+ fileinfo.inode = file->d_ino;
+ strncpy(fileinfo.name, file->d_name, sizeof(fileinfo.name));
+ fileinfo.name[sizeof(fileinfo.name) - 1] = '\0';
+
+ if (!pb_encode_tag_for_field(stream, field))
+ return false;
+
+ if (!pb_encode_submessage(stream, FileInfo_fields, &fileinfo))
+ return false;
+ }
+
+ return true;
+}
+
+void handle_connection(int connfd)
+{
+ ListFilesRequest request;
+ ListFilesResponse response;
+ pb_istream_t input = pb_istream_from_socket(connfd);
+ pb_ostream_t output = pb_ostream_from_socket(connfd);
+ DIR *directory;
+
+ if (!pb_decode(&input, ListFilesRequest_fields, &request))
+ {
+ printf("Decode failed: %s\n", PB_GET_ERROR(&input));
+ return;
+ }
+
+ directory = opendir(request.path);
+
+ printf("Listing directory: %s\n", request.path);
+
+ if (directory == NULL)
+ {
+ perror("opendir");
+
+ response.has_path_error = true;
+ response.path_error = true;
+ response.file.funcs.encode = NULL;
+ }
+ else
+ {
+ response.has_path_error = false;
+ response.file.funcs.encode = &listdir_callback;
+ response.file.arg = directory;
+ }
+
+ if (!pb_encode(&output, ListFilesResponse_fields, &response))
+ {
+ printf("Encoding failed.\n");
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int listenfd, connfd;
+ struct sockaddr_in servaddr;
+ int reuse = 1;
+
+ listenfd = socket(AF_INET, SOCK_STREAM, 0);
+
+ setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
+
+ memset(&servaddr, 0, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ servaddr.sin_port = htons(1234);
+ if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0)
+ {
+ perror("bind");
+ return 1;
+ }
+
+ if (listen(listenfd, 5) != 0)
+ {
+ perror("listen");
+ return 1;
+ }
+
+ for(;;)
+ {
+ connfd = accept(listenfd, NULL, NULL);
+
+ if (connfd < 0)
+ {
+ perror("accept");
+ return 1;
+ }
+
+ printf("Got connection.\n");
+
+ handle_connection(connfd);
+
+ printf("Closing connection.\n");
+
+ close(connfd);
+ }
+}
diff --git a/examples/using_double_on_avr/Makefile b/examples/using_double_on_avr/Makefile
new file mode 100644
index 00000000..0b5383fa
--- /dev/null
+++ b/examples/using_double_on_avr/Makefile
@@ -0,0 +1,29 @@
+CFLAGS = -Wall -Werror -g -O0
+
+# Path to the nanopb root directory
+NANOPB_DIR = ../..
+DEPS = double_conversion.c $(NANOPB_DIR)/pb.h \
+ $(NANOPB_DIR)/pb_decode.c $(NANOPB_DIR)/pb_decode.h \
+ $(NANOPB_DIR)/pb_encode.c $(NANOPB_DIR)/pb_encode.h
+CFLAGS += -I$(NANOPB_DIR)
+
+all: run_tests
+
+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 $(DEPS) doubleproto.pb.h doubleproto.pb.c
+ $(CC) $(CFLAGS) -o $@ $< double_conversion.c \
+ $(NANOPB_DIR)/pb_decode.c $(NANOPB_DIR)/pb_encode.c doubleproto.pb.c
+
+doubleproto.pb.c doubleproto.pb.h: doubleproto.proto $(NANOPB_DIR)/generator/nanopb_generator.py
+ protoc -odoubleproto.pb $<
+ python $(NANOPB_DIR)/generator/nanopb_generator.py doubleproto.pb
+
+run_tests: test_conversions encode_double decode_double
+ ./test_conversions
+ ./encode_double | ./decode_double
+
diff --git a/examples/using_double_on_avr/README b/examples/using_double_on_avr/README
new file mode 100644
index 00000000..d9fcdfc6
--- /dev/null
+++ b/examples/using_double_on_avr/README
@@ -0,0 +1,25 @@
+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
new file mode 100644
index 00000000..5802eca7
--- /dev/null
+++ b/examples/using_double_on_avr/decode_double.c
@@ -0,0 +1,33 @@
+/* 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
new file mode 100644
index 00000000..cf79b9a0
--- /dev/null
+++ b/examples/using_double_on_avr/double_conversion.c
@@ -0,0 +1,123 @@
+/* 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
new file mode 100644
index 00000000..62b6a8ae
--- /dev/null
+++ b/examples/using_double_on_avr/double_conversion.h
@@ -0,0 +1,26 @@
+/* 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
new file mode 100644
index 00000000..d8b7f2db
--- /dev/null
+++ b/examples/using_double_on_avr/doubleproto.proto
@@ -0,0 +1,13 @@
+// A message containing doubles, as used by other applications.
+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
new file mode 100644
index 00000000..cd532d46
--- /dev/null
+++ b/examples/using_double_on_avr/encode_double.c
@@ -0,0 +1,25 @@
+/* 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
new file mode 100644
index 00000000..22620a6a
--- /dev/null
+++ b/examples/using_double_on_avr/test_conversions.c
@@ -0,0 +1,56 @@
+#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/examples/using_union_messages/Makefile b/examples/using_union_messages/Makefile
new file mode 100644
index 00000000..0f7b5206
--- /dev/null
+++ b/examples/using_union_messages/Makefile
@@ -0,0 +1,22 @@
+CFLAGS = -ansi -Wall -Werror -g -O0
+
+# Path to the nanopb root folder
+NANOPB_DIR = ../..
+DEPS = $(NANOPB_DIR)/pb_decode.c $(NANOPB_DIR)/pb_decode.h \
+ $(NANOPB_DIR)/pb_encode.c $(NANOPB_DIR)/pb_encode.h $(NANOPB_DIR)/pb.h
+CFLAGS += -I$(NANOPB_DIR)
+
+all: encode decode
+ ./encode 1 | ./decode
+ ./encode 2 | ./decode
+ ./encode 3 | ./decode
+
+clean:
+ rm -f encode unionproto.pb.h unionproto.pb.c
+
+%: %.c $(DEPS) unionproto.pb.h unionproto.pb.c
+ $(CC) $(CFLAGS) -o $@ $< $(NANOPB_DIR)/pb_decode.c $(NANOPB_DIR)/pb_encode.c unionproto.pb.c
+
+unionproto.pb.h unionproto.pb.c: unionproto.proto $(NANOPB_DIR)/generator/nanopb_generator.py
+ protoc -ounionproto.pb $<
+ python $(NANOPB_DIR)/generator/nanopb_generator.py unionproto.pb
diff --git a/examples/using_union_messages/README b/examples/using_union_messages/README
new file mode 100644
index 00000000..7a1e75d4
--- /dev/null
+++ b/examples/using_union_messages/README
@@ -0,0 +1,52 @@
+Nanopb example "using_union_messages"
+=====================================
+
+Union messages is a common technique in Google Protocol Buffers used to
+represent a group of messages, only one of which is passed at a time.
+It is described in Google's documentation:
+https://developers.google.com/protocol-buffers/docs/techniques#union
+
+This directory contains an example on how to encode and decode union messages
+with minimal memory usage. Usually, nanopb would allocate space to store
+all of the possible messages at the same time, even though at most one of
+them will be used at a time.
+
+By using some of the lower level nanopb APIs, we can manually generate the
+top level message, so that we only need to allocate the one submessage that
+we actually want. Similarly when decoding, we can manually read the tag of
+the top level message, and only then allocate the memory for the submessage
+after we already know its type.
+
+
+Example usage
+-------------
+
+Type `make` to run the example. It will build it and run commands like
+following:
+
+./encode 1 | ./decode
+Got MsgType1: 42
+./encode 2 | ./decode
+Got MsgType2: true
+./encode 3 | ./decode
+Got MsgType3: 3 1415
+
+This simply demonstrates that the "decode" program has correctly identified
+the type of the received message, and managed to decode it.
+
+
+Details of implementation
+-------------------------
+
+unionproto.proto contains the protocol used in the example. It consists of
+three messages: MsgType1, MsgType2 and MsgType3, which are collected together
+into UnionMessage.
+
+encode.c takes one command line argument, which should be a number 1-3. It
+then fills in and encodes the corresponding message, and writes it to stdout.
+
+decode.c reads a UnionMessage from stdin. Then it calls the function
+decode_unionmessage_type() to determine the type of the message. After that,
+the corresponding message is decoded and the contents of it printed to the
+screen.
+
diff --git a/examples/using_union_messages/decode.c b/examples/using_union_messages/decode.c
new file mode 100644
index 00000000..b9f4af55
--- /dev/null
+++ b/examples/using_union_messages/decode.c
@@ -0,0 +1,96 @@
+/* This program reads a message from stdin, detects its type and decodes it.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <pb_decode.h>
+#include "unionproto.pb.h"
+
+/* This function reads manually the first tag from the stream and finds the
+ * corresponding message type. It doesn't yet decode the actual message.
+ *
+ * Returns a pointer to the MsgType_fields array, as an identifier for the
+ * message type. Returns null if the tag is of unknown type or an error occurs.
+ */
+const pb_field_t* decode_unionmessage_type(pb_istream_t *stream)
+{
+ pb_wire_type_t wire_type;
+ uint32_t tag;
+ bool eof;
+
+ while (pb_decode_tag(stream, &wire_type, &tag, &eof))
+ {
+ if (wire_type == PB_WT_STRING)
+ {
+ const pb_field_t *field;
+ for (field = UnionMessage_fields; field->tag != 0; field++)
+ {
+ if (field->tag == tag && (field->type & PB_LTYPE_SUBMESSAGE))
+ {
+ /* Found our field. */
+ return field->ptr;
+ }
+ }
+ }
+
+ /* Wasn't our field.. */
+ pb_skip_field(stream, wire_type);
+ }
+
+ return NULL;
+}
+
+bool decode_unionmessage_contents(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
+{
+ pb_istream_t substream;
+ bool status;
+ if (!pb_make_string_substream(stream, &substream))
+ return false;
+
+ status = pb_decode(&substream, fields, dest_struct);
+ pb_close_string_substream(stream, &substream);
+ return status;
+}
+
+int main()
+{
+ /* Read the data into buffer */
+ uint8_t buffer[512];
+ size_t count = fread(buffer, 1, sizeof(buffer), stdin);
+ pb_istream_t stream = pb_istream_from_buffer(buffer, count);
+
+ const pb_field_t *type = decode_unionmessage_type(&stream);
+ bool status = false;
+
+ if (type == MsgType1_fields)
+ {
+ MsgType1 msg = {};
+ status = decode_unionmessage_contents(&stream, MsgType1_fields, &msg);
+ printf("Got MsgType1: %d\n", msg.value);
+ }
+ else if (type == MsgType2_fields)
+ {
+ MsgType2 msg = {};
+ status = decode_unionmessage_contents(&stream, MsgType2_fields, &msg);
+ printf("Got MsgType2: %s\n", msg.value ? "true" : "false");
+ }
+ else if (type == MsgType3_fields)
+ {
+ MsgType3 msg = {};
+ status = decode_unionmessage_contents(&stream, MsgType3_fields, &msg);
+ printf("Got MsgType3: %d %d\n", msg.value1, msg.value2);
+ }
+
+ if (!status)
+ {
+ printf("Decode failed: %s\n", PB_GET_ERROR(&stream));
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
diff --git a/examples/using_union_messages/encode.c b/examples/using_union_messages/encode.c
new file mode 100644
index 00000000..e124bf91
--- /dev/null
+++ b/examples/using_union_messages/encode.c
@@ -0,0 +1,85 @@
+/* This program takes a command line argument and encodes a message in
+ * one of MsgType1, MsgType2 or MsgType3.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <pb_encode.h>
+#include "unionproto.pb.h"
+
+/* This function is the core of the union encoding process. It handles
+ * the top-level pb_field_t array manually, in order to encode a correct
+ * field tag before the message. The pointer to MsgType_fields array is
+ * used as an unique identifier for the message type.
+ */
+bool encode_unionmessage(pb_ostream_t *stream, const pb_field_t messagetype[], const void *message)
+{
+ const pb_field_t *field;
+ for (field = UnionMessage_fields; field->tag != 0; field++)
+ {
+ if (field->ptr == messagetype)
+ {
+ /* This is our field, encode the message using it. */
+ if (!pb_encode_tag_for_field(stream, field))
+ return false;
+
+ return pb_encode_submessage(stream, messagetype, message);
+ }
+ }
+
+ /* Didn't find the field for messagetype */
+ return false;
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 2)
+ {
+ fprintf(stderr, "Usage: %s (1|2|3)\n", argv[0]);
+ return 1;
+ }
+
+ uint8_t buffer[512];
+ pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+
+ bool status = false;
+ int msgtype = atoi(argv[1]);
+ if (msgtype == 1)
+ {
+ /* Send message of type 1 */
+ MsgType1 msg = {42};
+ status = encode_unionmessage(&stream, MsgType1_fields, &msg);
+ }
+ else if (msgtype == 2)
+ {
+ /* Send message of type 2 */
+ MsgType2 msg = {true};
+ status = encode_unionmessage(&stream, MsgType2_fields, &msg);
+ }
+ else if (msgtype == 3)
+ {
+ /* Send message of type 3 */
+ MsgType3 msg = {3, 1415};
+ status = encode_unionmessage(&stream, MsgType3_fields, &msg);
+ }
+ else
+ {
+ fprintf(stderr, "Unknown message type: %d\n", msgtype);
+ return 2;
+ }
+
+ if (!status)
+ {
+ fprintf(stderr, "Encoding failed!\n");
+ return 3;
+ }
+ else
+ {
+ fwrite(buffer, 1, stream.bytes_written, stdout);
+ return 0; /* Success */
+ }
+}
+
+
diff --git a/examples/using_union_messages/unionproto.proto b/examples/using_union_messages/unionproto.proto
new file mode 100644
index 00000000..d7c9de2d
--- /dev/null
+++ b/examples/using_union_messages/unionproto.proto
@@ -0,0 +1,30 @@
+// This is an example of how to handle 'union' style messages
+// with nanopb, without allocating memory for all the message types.
+//
+// There is no official type in Protocol Buffers for describing unions,
+// but they are commonly implemented by filling out exactly one of
+// several optional fields.
+
+message MsgType1
+{
+ required int32 value = 1;
+}
+
+message MsgType2
+{
+ required bool value = 1;
+}
+
+message MsgType3
+{
+ required int32 value1 = 1;
+ required int32 value2 = 2;
+}
+
+message UnionMessage
+{
+ optional MsgType1 msg1 = 1;
+ optional MsgType2 msg2 = 2;
+ optional MsgType3 msg3 = 3;
+}
+