aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/nanopb/examples
diff options
context:
space:
mode:
authorGravatar Nicolas "Pixel" Noble <pixel@nobis-crew.org>2016-09-23 06:28:04 +0200
committerGravatar Nicolas "Pixel" Noble <pixel@nobis-crew.org>2016-09-23 06:28:04 +0200
commit39937f73760b7cfc6be763d4b7e91b934df23fbe (patch)
tree11c90e51d1c93c82ab267b947cffff7eda0afbc4 /third_party/nanopb/examples
parentfe456e5b487717c25bde88f97c9029185130a2b9 (diff)
Putting nanopb files into third_party without being a submodule.
Diffstat (limited to 'third_party/nanopb/examples')
-rw-r--r--third_party/nanopb/examples/cmake_simple/CMakeLists.txt16
-rw-r--r--third_party/nanopb/examples/cmake_simple/README.txt18
-rw-r--r--third_party/nanopb/examples/cmake_simple/simple.c71
-rw-r--r--third_party/nanopb/examples/cmake_simple/simple.proto9
-rw-r--r--third_party/nanopb/examples/network_server/Makefile17
-rw-r--r--third_party/nanopb/examples/network_server/README.txt60
-rw-r--r--third_party/nanopb/examples/network_server/client.c142
-rw-r--r--third_party/nanopb/examples/network_server/common.c40
-rw-r--r--third_party/nanopb/examples/network_server/common.h9
-rw-r--r--third_party/nanopb/examples/network_server/fileproto.options13
-rw-r--r--third_party/nanopb/examples/network_server/fileproto.proto20
-rw-r--r--third_party/nanopb/examples/network_server/server.c158
-rw-r--r--third_party/nanopb/examples/simple/Makefile22
-rw-r--r--third_party/nanopb/examples/simple/README.txt29
-rw-r--r--third_party/nanopb/examples/simple/simple.c71
-rw-r--r--third_party/nanopb/examples/simple/simple.proto9
-rw-r--r--third_party/nanopb/examples/using_double_on_avr/Makefile24
-rw-r--r--third_party/nanopb/examples/using_double_on_avr/README.txt25
-rw-r--r--third_party/nanopb/examples/using_double_on_avr/decode_double.c33
-rw-r--r--third_party/nanopb/examples/using_double_on_avr/double_conversion.c123
-rw-r--r--third_party/nanopb/examples/using_double_on_avr/double_conversion.h26
-rw-r--r--third_party/nanopb/examples/using_double_on_avr/doubleproto.proto15
-rw-r--r--third_party/nanopb/examples/using_double_on_avr/encode_double.c25
-rw-r--r--third_party/nanopb/examples/using_double_on_avr/test_conversions.c56
-rw-r--r--third_party/nanopb/examples/using_union_messages/Makefile20
-rw-r--r--third_party/nanopb/examples/using_union_messages/README.txt52
-rw-r--r--third_party/nanopb/examples/using_union_messages/decode.c96
-rw-r--r--third_party/nanopb/examples/using_union_messages/encode.c85
-rw-r--r--third_party/nanopb/examples/using_union_messages/unionproto.proto32
29 files changed, 1316 insertions, 0 deletions
diff --git a/third_party/nanopb/examples/cmake_simple/CMakeLists.txt b/third_party/nanopb/examples/cmake_simple/CMakeLists.txt
new file mode 100644
index 0000000000..e5f33a028e
--- /dev/null
+++ b/third_party/nanopb/examples/cmake_simple/CMakeLists.txt
@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 2.8)
+project(NANOPB_CMAKE_SIMPLE C)
+
+set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../extra)
+find_package(Nanopb REQUIRED)
+include_directories(${NANOPB_INCLUDE_DIRS})
+
+nanopb_generate_cpp(PROTO_SRCS PROTO_HDRS simple.proto)
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+#add_custom_target(generate_proto_sources DEPENDS ${PROTO_SRCS} ${PROTO_HDRS})
+set_source_files_properties(${PROTO_SRCS} ${PROTO_HDRS}
+ PROPERTIES GENERATED TRUE)
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -g -O0")
+
+add_executable(simple simple.c ${PROTO_SRCS} ${PROTO_HDRS})
diff --git a/third_party/nanopb/examples/cmake_simple/README.txt b/third_party/nanopb/examples/cmake_simple/README.txt
new file mode 100644
index 0000000000..aa0f3f3a77
--- /dev/null
+++ b/third_party/nanopb/examples/cmake_simple/README.txt
@@ -0,0 +1,18 @@
+Nanopb example "simple" using CMake
+=======================
+
+This example is the same as the simple nanopb example but built using CMake.
+
+Example usage
+-------------
+
+On Linux, create a build directory and then call cmake:
+
+ nanopb/examples/cmake_simple$ mkdir build
+ nanopb/examples/cmake_simple$ cd build/
+ nanopb/examples/cmake_simple/build$ cmake ..
+ nanopb/examples/cmake_simple/build$ make
+
+After that, you can run it with the command: ./simple
+
+On other platforms supported by CMake, refer to CMake instructions.
diff --git a/third_party/nanopb/examples/cmake_simple/simple.c b/third_party/nanopb/examples/cmake_simple/simple.c
new file mode 100644
index 0000000000..1f6b137351
--- /dev/null
+++ b/third_party/nanopb/examples/cmake_simple/simple.c
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <pb_encode.h>
+#include <pb_decode.h>
+#include "simple.pb.h"
+
+int main()
+{
+ /* This is the buffer where we will store our message. */
+ uint8_t buffer[128];
+ size_t message_length;
+ bool status;
+
+ /* Encode our message */
+ {
+ /* Allocate space on the stack to store the message data.
+ *
+ * Nanopb generates simple struct definitions for all the messages.
+ * - check out the contents of simple.pb.h!
+ * It is a good idea to always initialize your structures
+ * so that you do not have garbage data from RAM in there.
+ */
+ SimpleMessage message = SimpleMessage_init_zero;
+
+ /* Create a stream that will write to our buffer. */
+ pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+
+ /* Fill in the lucky number */
+ message.lucky_number = 13;
+
+ /* Now we are ready to encode the message! */
+ status = pb_encode(&stream, SimpleMessage_fields, &message);
+ message_length = stream.bytes_written;
+
+ /* Then just check for any errors.. */
+ if (!status)
+ {
+ printf("Encoding failed: %s\n", PB_GET_ERROR(&stream));
+ return 1;
+ }
+ }
+
+ /* Now we could transmit the message over network, store it in a file or
+ * wrap it to a pigeon's leg.
+ */
+
+ /* But because we are lazy, we will just decode it immediately. */
+
+ {
+ /* Allocate space for the decoded message. */
+ SimpleMessage message = SimpleMessage_init_zero;
+
+ /* Create a stream that reads from the buffer. */
+ pb_istream_t stream = pb_istream_from_buffer(buffer, message_length);
+
+ /* Now we are ready to decode the message. */
+ status = pb_decode(&stream, SimpleMessage_fields, &message);
+
+ /* Check for errors... */
+ if (!status)
+ {
+ printf("Decoding failed: %s\n", PB_GET_ERROR(&stream));
+ return 1;
+ }
+
+ /* Print the data contained in the message. */
+ printf("Your lucky number was %d!\n", message.lucky_number);
+ }
+
+ return 0;
+}
+
diff --git a/third_party/nanopb/examples/cmake_simple/simple.proto b/third_party/nanopb/examples/cmake_simple/simple.proto
new file mode 100644
index 0000000000..5c73a3b229
--- /dev/null
+++ b/third_party/nanopb/examples/cmake_simple/simple.proto
@@ -0,0 +1,9 @@
+// A very simple protocol definition, consisting of only
+// one message.
+
+syntax = "proto2";
+
+message SimpleMessage {
+ required int32 lucky_number = 1;
+}
+
diff --git a/third_party/nanopb/examples/network_server/Makefile b/third_party/nanopb/examples/network_server/Makefile
new file mode 100644
index 0000000000..2c7639a15d
--- /dev/null
+++ b/third_party/nanopb/examples/network_server/Makefile
@@ -0,0 +1,17 @@
+# Include the nanopb provided Makefile rules
+include ../../extra/nanopb.mk
+
+# Compiler flags to enable all warnings & debug info
+CFLAGS = -ansi -Wall -Werror -g -O0
+CFLAGS += -I$(NANOPB_DIR)
+
+all: server client
+
+.SUFFIXES:
+
+clean:
+ rm -f server client fileproto.pb.c fileproto.pb.h
+
+%: %.c common.c fileproto.pb.c
+ $(CC) $(CFLAGS) -o $@ $^ $(NANOPB_CORE)
+
diff --git a/third_party/nanopb/examples/network_server/README.txt b/third_party/nanopb/examples/network_server/README.txt
new file mode 100644
index 0000000000..7bdcbed5db
--- /dev/null
+++ b/third_party/nanopb/examples/network_server/README.txt
@@ -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/third_party/nanopb/examples/network_server/client.c b/third_party/nanopb/examples/network_server/client.c
new file mode 100644
index 0000000000..00f6dab855
--- /dev/null
+++ b/third_party/nanopb/examples/network_server/client.c
@@ -0,0 +1,142 @@
+/* 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"
+
+/* This callback function will be called once for each filename received
+ * from the server. The filenames will be printed out immediately, so that
+ * no memory has to be allocated for them.
+ */
+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;
+}
+
+/* This function sends a request to socket 'fd' to list the files in
+ * directory given in 'path'. The results received from server will
+ * be printed to stdout.
+ */
+bool listdir(int fd, char *path)
+{
+ /* Construct and send the request to server */
+ {
+ ListFilesRequest request = {};
+ pb_ostream_t output = pb_ostream_from_socket(fd);
+ uint8_t zero = 0;
+
+ /* In our protocol, path is optional. If it is not given,
+ * the server will list the root directory. */
+ 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);
+ }
+
+ /* Encode the request. It is written to the socket immediately
+ * through our custom stream. */
+ if (!pb_encode(&output, ListFilesRequest_fields, &request))
+ {
+ fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&output));
+ return false;
+ }
+
+ /* We signal the end of request with a 0 tag. */
+ pb_write(&output, &zero, 1);
+ }
+
+ /* Read back the response from server */
+ {
+ ListFilesResponse response = {};
+ pb_istream_t input = pb_istream_from_socket(fd);
+
+ /* Give a pointer to our callback function, which will handle the
+ * filenames as they arrive. */
+ 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 the message from server decodes properly, but directory was
+ * not found on server side, we get path_error == true. */
+ 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);
+
+ /* Connect to server running on localhost:1234 */
+ 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;
+ }
+
+ /* Send the directory listing request */
+ if (!listdir(sockfd, path))
+ return 2;
+
+ /* Close connection */
+ close(sockfd);
+
+ return 0;
+}
diff --git a/third_party/nanopb/examples/network_server/common.c b/third_party/nanopb/examples/network_server/common.c
new file mode 100644
index 0000000000..04a5aa85c0
--- /dev/null
+++ b/third_party/nanopb/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/third_party/nanopb/examples/network_server/common.h b/third_party/nanopb/examples/network_server/common.h
new file mode 100644
index 0000000000..8dab3b7c38
--- /dev/null
+++ b/third_party/nanopb/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/third_party/nanopb/examples/network_server/fileproto.options b/third_party/nanopb/examples/network_server/fileproto.options
new file mode 100644
index 0000000000..29a2ab0e4a
--- /dev/null
+++ b/third_party/nanopb/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/third_party/nanopb/examples/network_server/fileproto.proto b/third_party/nanopb/examples/network_server/fileproto.proto
new file mode 100644
index 0000000000..5640b8d501
--- /dev/null
+++ b/third_party/nanopb/examples/network_server/fileproto.proto
@@ -0,0 +1,20 @@
+// This defines protocol for a simple server that lists files.
+//
+// See also the nanopb-specific options in fileproto.options.
+
+syntax = "proto2";
+
+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/third_party/nanopb/examples/network_server/server.c b/third_party/nanopb/examples/network_server/server.c
new file mode 100644
index 0000000000..46a5f38d1d
--- /dev/null
+++ b/third_party/nanopb/examples/network_server/server.c
@@ -0,0 +1,158 @@
+/* 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"
+
+/* This callback function will be called once during the encoding.
+ * It will write out any number of FileInfo entries, without consuming unnecessary memory.
+ * This is accomplished by fetching the filenames one at a time and encoding them
+ * immediately.
+ */
+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';
+
+ /* This encodes the header for the field, based on the constant info
+ * from pb_field_t. */
+ if (!pb_encode_tag_for_field(stream, field))
+ return false;
+
+ /* This encodes the data for the field, based on our FileInfo structure. */
+ if (!pb_encode_submessage(stream, FileInfo_fields, &fileinfo))
+ return false;
+ }
+
+ return true;
+}
+
+/* Handle one arriving client connection.
+ * Clients are expected to send a ListFilesRequest, terminated by a '0'.
+ * Server will respond with a ListFilesResponse message.
+ */
+void handle_connection(int connfd)
+{
+ DIR *directory = NULL;
+
+ /* Decode the message from the client and open the requested directory. */
+ {
+ ListFilesRequest request = {};
+ pb_istream_t input = pb_istream_from_socket(connfd);
+
+ 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);
+ }
+
+ /* List the files in the directory and transmit the response to client */
+ {
+ ListFilesResponse response = {};
+ pb_ostream_t output = pb_ostream_from_socket(connfd);
+
+ if (directory == NULL)
+ {
+ perror("opendir");
+
+ /* Directory was not found, transmit error status */
+ response.has_path_error = true;
+ response.path_error = true;
+ response.file.funcs.encode = NULL;
+ }
+ else
+ {
+ /* Directory was found, transmit filenames */
+ 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: %s\n", PB_GET_ERROR(&output));
+ }
+ }
+
+ if (directory != NULL)
+ closedir(directory);
+}
+
+int main(int argc, char **argv)
+{
+ int listenfd, connfd;
+ struct sockaddr_in servaddr;
+ int reuse = 1;
+
+ /* Listen on localhost:1234 for TCP connections */
+ 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(;;)
+ {
+ /* Wait for a client */
+ 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);
+ }
+
+ return 0;
+}
diff --git a/third_party/nanopb/examples/simple/Makefile b/third_party/nanopb/examples/simple/Makefile
new file mode 100644
index 0000000000..970a865009
--- /dev/null
+++ b/third_party/nanopb/examples/simple/Makefile
@@ -0,0 +1,22 @@
+# 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)
+
+# C source code files that are required
+CSRC = simple.c # The main program
+CSRC += simple.pb.c # The compiled protocol definition
+CSRC += $(NANOPB_DIR)/pb_encode.c # The nanopb encoder
+CSRC += $(NANOPB_DIR)/pb_decode.c # The nanopb decoder
+CSRC += $(NANOPB_DIR)/pb_common.c # The nanopb common parts
+
+# Build rule for the main program
+simple: $(CSRC)
+ $(CC) $(CFLAGS) -osimple $(CSRC)
+
+# Build rule for the protocol
+simple.pb.c: simple.proto
+ $(PROTOC) $(PROTOC_OPTS) --nanopb_out=. simple.proto
+
diff --git a/third_party/nanopb/examples/simple/README.txt b/third_party/nanopb/examples/simple/README.txt
new file mode 100644
index 0000000000..ee77bfc70c
--- /dev/null
+++ b/third_party/nanopb/examples/simple/README.txt
@@ -0,0 +1,29 @@
+Nanopb example "simple"
+=======================
+
+This example demonstrates the very basic use of nanopb. It encodes and
+decodes a simple message.
+
+The code uses four different API functions:
+
+ * pb_ostream_from_buffer() to declare the output buffer that is to be used
+ * pb_encode() to encode a message
+ * pb_istream_from_buffer() to declare the input buffer that is to be used
+ * pb_decode() to decode a message
+
+Example usage
+-------------
+
+On Linux, simply type "make" to build the example. After that, you can
+run it with the command: ./simple
+
+On other platforms, you first have to compile the protocol definition using
+the following command::
+
+ ../../generator-bin/protoc --nanopb_out=. simple.proto
+
+After that, add the following four files to your project and compile:
+
+ simple.c simple.pb.c pb_encode.c pb_decode.c
+
+
diff --git a/third_party/nanopb/examples/simple/simple.c b/third_party/nanopb/examples/simple/simple.c
new file mode 100644
index 0000000000..1f6b137351
--- /dev/null
+++ b/third_party/nanopb/examples/simple/simple.c
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <pb_encode.h>
+#include <pb_decode.h>
+#include "simple.pb.h"
+
+int main()
+{
+ /* This is the buffer where we will store our message. */
+ uint8_t buffer[128];
+ size_t message_length;
+ bool status;
+
+ /* Encode our message */
+ {
+ /* Allocate space on the stack to store the message data.
+ *
+ * Nanopb generates simple struct definitions for all the messages.
+ * - check out the contents of simple.pb.h!
+ * It is a good idea to always initialize your structures
+ * so that you do not have garbage data from RAM in there.
+ */
+ SimpleMessage message = SimpleMessage_init_zero;
+
+ /* Create a stream that will write to our buffer. */
+ pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+
+ /* Fill in the lucky number */
+ message.lucky_number = 13;
+
+ /* Now we are ready to encode the message! */
+ status = pb_encode(&stream, SimpleMessage_fields, &message);
+ message_length = stream.bytes_written;
+
+ /* Then just check for any errors.. */
+ if (!status)
+ {
+ printf("Encoding failed: %s\n", PB_GET_ERROR(&stream));
+ return 1;
+ }
+ }
+
+ /* Now we could transmit the message over network, store it in a file or
+ * wrap it to a pigeon's leg.
+ */
+
+ /* But because we are lazy, we will just decode it immediately. */
+
+ {
+ /* Allocate space for the decoded message. */
+ SimpleMessage message = SimpleMessage_init_zero;
+
+ /* Create a stream that reads from the buffer. */
+ pb_istream_t stream = pb_istream_from_buffer(buffer, message_length);
+
+ /* Now we are ready to decode the message. */
+ status = pb_decode(&stream, SimpleMessage_fields, &message);
+
+ /* Check for errors... */
+ if (!status)
+ {
+ printf("Decoding failed: %s\n", PB_GET_ERROR(&stream));
+ return 1;
+ }
+
+ /* Print the data contained in the message. */
+ printf("Your lucky number was %d!\n", message.lucky_number);
+ }
+
+ return 0;
+}
+
diff --git a/third_party/nanopb/examples/simple/simple.proto b/third_party/nanopb/examples/simple/simple.proto
new file mode 100644
index 0000000000..5c73a3b229
--- /dev/null
+++ b/third_party/nanopb/examples/simple/simple.proto
@@ -0,0 +1,9 @@
+// A very simple protocol definition, consisting of only
+// one message.
+
+syntax = "proto2";
+
+message SimpleMessage {
+ required int32 lucky_number = 1;
+}
+
diff --git a/third_party/nanopb/examples/using_double_on_avr/Makefile b/third_party/nanopb/examples/using_double_on_avr/Makefile
new file mode 100644
index 0000000000..874a64bdc9
--- /dev/null
+++ b/third_party/nanopb/examples/using_double_on_avr/Makefile
@@ -0,0 +1,24 @@
+# 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/third_party/nanopb/examples/using_double_on_avr/README.txt b/third_party/nanopb/examples/using_double_on_avr/README.txt
new file mode 100644
index 0000000000..d9fcdfc66d
--- /dev/null
+++ b/third_party/nanopb/examples/using_double_on_avr/README.txt
@@ -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/third_party/nanopb/examples/using_double_on_avr/decode_double.c b/third_party/nanopb/examples/using_double_on_avr/decode_double.c
new file mode 100644
index 0000000000..5802eca79e
--- /dev/null
+++ b/third_party/nanopb/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/third_party/nanopb/examples/using_double_on_avr/double_conversion.c b/third_party/nanopb/examples/using_double_on_avr/double_conversion.c
new file mode 100644
index 0000000000..cf79b9a00d
--- /dev/null
+++ b/third_party/nanopb/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/third_party/nanopb/examples/using_double_on_avr/double_conversion.h b/third_party/nanopb/examples/using_double_on_avr/double_conversion.h
new file mode 100644
index 0000000000..62b6a8ae8d
--- /dev/null
+++ b/third_party/nanopb/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/third_party/nanopb/examples/using_double_on_avr/doubleproto.proto b/third_party/nanopb/examples/using_double_on_avr/doubleproto.proto
new file mode 100644
index 0000000000..72d3f9c127
--- /dev/null
+++ b/third_party/nanopb/examples/using_double_on_avr/doubleproto.proto
@@ -0,0 +1,15 @@
+// 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/third_party/nanopb/examples/using_double_on_avr/encode_double.c b/third_party/nanopb/examples/using_double_on_avr/encode_double.c
new file mode 100644
index 0000000000..cd532d4659
--- /dev/null
+++ b/third_party/nanopb/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/third_party/nanopb/examples/using_double_on_avr/test_conversions.c b/third_party/nanopb/examples/using_double_on_avr/test_conversions.c
new file mode 100644
index 0000000000..22620a6ae9
--- /dev/null
+++ b/third_party/nanopb/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/third_party/nanopb/examples/using_union_messages/Makefile b/third_party/nanopb/examples/using_union_messages/Makefile
new file mode 100644
index 0000000000..66396a02ee
--- /dev/null
+++ b/third_party/nanopb/examples/using_union_messages/Makefile
@@ -0,0 +1,20 @@
+# Include the nanopb provided Makefile rules
+include ../../extra/nanopb.mk
+
+# Compiler flags to enable all warnings & debug info
+CFLAGS = -ansi -Wall -Werror -g -O0
+CFLAGS += -I$(NANOPB_DIR)
+
+all: encode decode
+ ./encode 1 | ./decode
+ ./encode 2 | ./decode
+ ./encode 3 | ./decode
+
+.SUFFIXES:
+
+clean:
+ rm -f encode unionproto.pb.h unionproto.pb.c
+
+%: %.c unionproto.pb.c
+ $(CC) $(CFLAGS) -o $@ $^ $(NANOPB_CORE)
+
diff --git a/third_party/nanopb/examples/using_union_messages/README.txt b/third_party/nanopb/examples/using_union_messages/README.txt
new file mode 100644
index 0000000000..7a1e75d411
--- /dev/null
+++ b/third_party/nanopb/examples/using_union_messages/README.txt
@@ -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/third_party/nanopb/examples/using_union_messages/decode.c b/third_party/nanopb/examples/using_union_messages/decode.c
new file mode 100644
index 0000000000..b9f4af55c5
--- /dev/null
+++ b/third_party/nanopb/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/third_party/nanopb/examples/using_union_messages/encode.c b/third_party/nanopb/examples/using_union_messages/encode.c
new file mode 100644
index 0000000000..e124bf91fa
--- /dev/null
+++ b/third_party/nanopb/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/third_party/nanopb/examples/using_union_messages/unionproto.proto b/third_party/nanopb/examples/using_union_messages/unionproto.proto
new file mode 100644
index 0000000000..209df0d27a
--- /dev/null
+++ b/third_party/nanopb/examples/using_union_messages/unionproto.proto
@@ -0,0 +1,32 @@
+// 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.
+
+syntax = "proto2";
+
+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;
+}
+