├── .gitignore ├── BUILD ├── README.md ├── WORKSPACE ├── demo.jinja ├── examples ├── chibios │ ├── .gitignore │ ├── README.md │ ├── STM32F302R8-Nucleo │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── chconf.h │ │ ├── halconf.h │ │ ├── main.c │ │ ├── mcuconf.h │ │ └── openocd.cfg │ └── port.c └── posix │ ├── demo.c │ ├── demo_watchgroups.c │ ├── port.c │ └── port.h ├── messagebus.c ├── messagebus.h ├── messagebus_cpp.hpp ├── package.yml └── tests ├── atomicity.cpp ├── foreach.cpp ├── mocks ├── synchronization.cpp └── synchronization.hpp ├── msgbus.cpp ├── new_topic_callbacks.cpp ├── signaling.cpp ├── test_cpp_interface.cpp └── watchgroups.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Cmake build directory 32 | build/ 33 | 34 | # Packager generated 35 | CMakeLists.txt 36 | -------------------------------------------------------------------------------- /BUILD: -------------------------------------------------------------------------------- 1 | cc_library( 2 | name = "msgbus", 3 | srcs = [ 4 | "messagebus.c", 5 | "messagebus.h", 6 | "messagebus_cpp.hpp", 7 | ], 8 | hdrs = ["messagebus.h"], 9 | include_prefix = "msgbus", 10 | visibility = ["//visibility:public"], 11 | # TODO: Add ChibiOS port 12 | deps = select({ 13 | "//conditions:default": [":msgbus-pthread"], 14 | }), 15 | ) 16 | 17 | cc_test( 18 | name = "msgbus-test", 19 | size = "small", 20 | srcs = glob([ 21 | "tests/*.cpp", 22 | "tests/mocks/*.cpp", 23 | "tests/mocks/*.hpp", 24 | ]), 25 | deps = [ 26 | ":msgbus", 27 | "@ch_cvra_test_runner//:main", 28 | ], 29 | ) 30 | 31 | cc_library( 32 | name = "msgbus-pthread", 33 | srcs = [ 34 | "examples/posix/port.c", 35 | "messagebus.h", 36 | ], 37 | hdrs = ["examples/posix/port.h"], 38 | linkopts = ["-pthread"], 39 | ) 40 | 41 | cc_binary( 42 | name = "msgbus-demo", 43 | srcs = ["examples/posix/demo.c"], 44 | deps = [":msgbus"], 45 | ) 46 | 47 | cc_binary( 48 | name = "msgbus-demo-watchgroups", 49 | srcs = ["examples/posix/demo_watchgroups.c"], 50 | deps = [":msgbus"], 51 | ) 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Microcontroller message bus 2 | 3 | ## Features 4 | 5 | * Runtime declaration of topics 6 | * Many publishers, many subscribers (N to M). 7 | * Subscribers and publishers can be removed without impacting bus. 8 | * Can block waiting for a message. 9 | * Can poll to see if there was an update to the message. 10 | * Topics are atomic. 11 | * Different serialization methods are possible. 12 | * Each topic can have a metadata block. 13 | It can be used to contain function pointers to serialization / deserialization methods for example. 14 | Metadata do not offer the same atomicity guarantees as the topic data themselves. 15 | * Possibility to register callbacks that are triggered on topic creation. 16 | 17 | ## Features that won't be supported 18 | 19 | The following features won't be supported, to keep the codebase simple. 20 | 21 | * Runtime deletion of topics 22 | 23 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | workspace(name = "ch_cvra_msgbus") 2 | -------------------------------------------------------------------------------- /demo.jinja: -------------------------------------------------------------------------------- 1 | {% extends "CMakeLists.txt.jinja" %} 2 | 3 | {% block additional_targets %} 4 | add_executable( 5 | demo 6 | {% for s in source + target.demo -%} 7 | {{ s }} 8 | {% endfor %} 9 | ) 10 | 11 | target_link_libraries( 12 | demo 13 | pthread 14 | ) 15 | 16 | add_executable( 17 | demo_watchgroups 18 | {% for s in source + target.demo_watchgroups -%} 19 | {{ s }} 20 | {% endfor %} 21 | ) 22 | 23 | target_link_libraries( 24 | demo 25 | pthread 26 | ) 27 | 28 | 29 | 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /examples/chibios/.gitignore: -------------------------------------------------------------------------------- 1 | ChibiOS 2 | -------------------------------------------------------------------------------- /examples/chibios/README.md: -------------------------------------------------------------------------------- 1 | # ChibiOS examples 2 | 3 | This directory contains examples to use with ChibiOS. 4 | You should first clone it in this repository: 5 | 6 | ```bash 7 | git clone https://github.com/cvra/ChibiOS.git 8 | git checkout 3.0.1-cvra 9 | ``` 10 | -------------------------------------------------------------------------------- /examples/chibios/STM32F302R8-Nucleo/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .dep 3 | -------------------------------------------------------------------------------- /examples/chibios/STM32F302R8-Nucleo/Makefile: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | # Build global options 3 | # NOTE: Can be overridden externally. 4 | # 5 | 6 | # Compiler options here. 7 | ifeq ($(USE_OPT),) 8 | USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16 9 | endif 10 | 11 | # C specific options here (added to USE_OPT). 12 | ifeq ($(USE_COPT),) 13 | USE_COPT = 14 | endif 15 | 16 | # C++ specific options here (added to USE_OPT). 17 | ifeq ($(USE_CPPOPT),) 18 | USE_CPPOPT = -fno-rtti 19 | endif 20 | 21 | # Enable this if you want the linker to remove unused code and data 22 | ifeq ($(USE_LINK_GC),) 23 | USE_LINK_GC = yes 24 | endif 25 | 26 | # Linker extra options here. 27 | ifeq ($(USE_LDOPT),) 28 | USE_LDOPT = 29 | endif 30 | 31 | # Enable this if you want link time optimizations (LTO) 32 | ifeq ($(USE_LTO),) 33 | USE_LTO = yes 34 | endif 35 | 36 | # If enabled, this option allows to compile the application in THUMB mode. 37 | ifeq ($(USE_THUMB),) 38 | USE_THUMB = yes 39 | endif 40 | 41 | # Enable this if you want to see the full log while compiling. 42 | ifeq ($(USE_VERBOSE_COMPILE),) 43 | USE_VERBOSE_COMPILE = no 44 | endif 45 | 46 | # If enabled, this option makes the build process faster by not compiling 47 | # modules not used in the current configuration. 48 | ifeq ($(USE_SMART_BUILD),) 49 | USE_SMART_BUILD = yes 50 | endif 51 | 52 | # 53 | # Build global options 54 | ############################################################################## 55 | 56 | ############################################################################## 57 | # Architecture or project specific options 58 | # 59 | 60 | # Stack size to be allocated to the Cortex-M process stack. This stack is 61 | # the stack used by the main() thread. 62 | ifeq ($(USE_PROCESS_STACKSIZE),) 63 | USE_PROCESS_STACKSIZE = 0x400 64 | endif 65 | 66 | # Stack size to the allocated to the Cortex-M main/exceptions stack. This 67 | # stack is used for processing interrupts and exceptions. 68 | ifeq ($(USE_EXCEPTIONS_STACKSIZE),) 69 | USE_EXCEPTIONS_STACKSIZE = 0x400 70 | endif 71 | 72 | # Enables the use of FPU on Cortex-M4 (no, softfp, hard). 73 | ifeq ($(USE_FPU),) 74 | USE_FPU = no 75 | endif 76 | 77 | # 78 | # Architecture or project specific options 79 | ############################################################################## 80 | 81 | ############################################################################## 82 | # Project, sources and paths 83 | # 84 | 85 | # Define project name here 86 | PROJECT = ch 87 | 88 | # Imported source files and paths 89 | CHIBIOS = ../ChibiOS 90 | # Startup files. 91 | include $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/startup_stm32f3xx.mk 92 | # HAL-OSAL files (optional). 93 | include $(CHIBIOS)/os/hal/hal.mk 94 | include $(CHIBIOS)/os/hal/ports/STM32/STM32F3xx/platform.mk 95 | include $(CHIBIOS)/os/hal/boards/ST_NUCLEO_F302R8/board.mk 96 | include $(CHIBIOS)/os/hal/osal/rt/osal.mk 97 | # RTOS files (optional). 98 | include $(CHIBIOS)/os/rt/rt.mk 99 | include $(CHIBIOS)/os/rt/ports/ARMCMx/compilers/GCC/mk/port_v7m.mk 100 | # Other files (optional). 101 | include $(CHIBIOS)/test/rt/test.mk 102 | 103 | # Define linker script file here 104 | LDSCRIPT= $(STARTUPLD)/STM32F302x8.ld 105 | 106 | # C sources that can be compiled in ARM or THUMB mode depending on the global 107 | # setting. 108 | CSRC = $(STARTUPSRC) \ 109 | $(KERNSRC) \ 110 | $(PORTSRC) \ 111 | $(OSALSRC) \ 112 | $(HALSRC) \ 113 | $(PLATFORMSRC) \ 114 | $(BOARDSRC) \ 115 | $(TESTSRC) \ 116 | main.c \ 117 | ../port.c \ 118 | ../../../messagebus.c 119 | 120 | 121 | # C++ sources that can be compiled in ARM or THUMB mode depending on the global 122 | # setting. 123 | CPPSRC = 124 | 125 | # C sources to be compiled in ARM mode regardless of the global setting. 126 | # NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler 127 | # option that results in lower performance and larger code size. 128 | ACSRC = 129 | 130 | # C++ sources to be compiled in ARM mode regardless of the global setting. 131 | # NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler 132 | # option that results in lower performance and larger code size. 133 | ACPPSRC = 134 | 135 | # C sources to be compiled in THUMB mode regardless of the global setting. 136 | # NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler 137 | # option that results in lower performance and larger code size. 138 | TCSRC = 139 | 140 | # C sources to be compiled in THUMB mode regardless of the global setting. 141 | # NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler 142 | # option that results in lower performance and larger code size. 143 | TCPPSRC = 144 | 145 | # List ASM source files here 146 | ASMSRC = $(STARTUPASM) $(PORTASM) $(OSALASM) 147 | 148 | INCDIR = $(STARTUPINC) $(KERNINC) $(PORTINC) $(OSALINC) \ 149 | $(HALINC) $(PLATFORMINC) $(BOARDINC) $(TESTINC) \ 150 | $(CHIBIOS)/os/various 151 | 152 | # 153 | # Project, sources and paths 154 | ############################################################################## 155 | 156 | ############################################################################## 157 | # Compiler settings 158 | # 159 | 160 | MCU = cortex-m4 161 | 162 | #TRGT = arm-elf- 163 | TRGT = arm-none-eabi- 164 | CC = $(TRGT)gcc 165 | CPPC = $(TRGT)g++ 166 | # Enable loading with g++ only if you need C++ runtime support. 167 | # NOTE: You can use C++ even without C++ support if you are careful. C++ 168 | # runtime support makes code size explode. 169 | LD = $(TRGT)gcc 170 | #LD = $(TRGT)g++ 171 | CP = $(TRGT)objcopy 172 | AS = $(TRGT)gcc -x assembler-with-cpp 173 | AR = $(TRGT)ar 174 | OD = $(TRGT)objdump 175 | SZ = $(TRGT)size 176 | HEX = $(CP) -O ihex 177 | BIN = $(CP) -O binary 178 | 179 | # ARM-specific options here 180 | AOPT = 181 | 182 | # THUMB-specific options here 183 | TOPT = -mthumb -DTHUMB 184 | 185 | # Define C warning options here 186 | CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes 187 | 188 | # Define C++ warning options here 189 | CPPWARN = -Wall -Wextra -Wundef 190 | 191 | # 192 | # Compiler settings 193 | ############################################################################## 194 | 195 | ############################################################################## 196 | # Start of user section 197 | # 198 | 199 | # List all user C define here, like -D_DEBUG=1 200 | UDEFS = 201 | 202 | # Define ASM defines here 203 | UADEFS = 204 | 205 | # List all user directories here 206 | UINCDIR = 207 | 208 | # List the user directory to look for the libraries here 209 | ULIBDIR = 210 | 211 | # List all user libraries here 212 | ULIBS = 213 | 214 | # 215 | # End of user defines 216 | ############################################################################## 217 | 218 | RULESPATH = $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC 219 | include $(RULESPATH)/rules.mk 220 | 221 | .PHONY: flash 222 | flash: build/$(PROJECT).elf 223 | openocd -f openocd.cfg -c "program build/$(PROJECT).elf verify reset" -c "shutdown" 224 | -------------------------------------------------------------------------------- /examples/chibios/STM32F302R8-Nucleo/chconf.h: -------------------------------------------------------------------------------- 1 | /* 2 | ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /** 18 | * @file templates/chconf.h 19 | * @brief Configuration file template. 20 | * @details A copy of this file must be placed in each project directory, it 21 | * contains the application specific kernel settings. 22 | * 23 | * @addtogroup config 24 | * @details Kernel related settings and hooks. 25 | * @{ 26 | */ 27 | 28 | #ifndef _CHCONF_H_ 29 | #define _CHCONF_H_ 30 | 31 | /*===========================================================================*/ 32 | /** 33 | * @name System timers settings 34 | * @{ 35 | */ 36 | /*===========================================================================*/ 37 | 38 | /** 39 | * @brief System time counter resolution. 40 | * @note Allowed values are 16 or 32 bits. 41 | */ 42 | #define CH_CFG_ST_RESOLUTION 32 43 | 44 | /** 45 | * @brief System tick frequency. 46 | * @details Frequency of the system timer that drives the system ticks. This 47 | * setting also defines the system tick time unit. 48 | */ 49 | #define CH_CFG_ST_FREQUENCY 10000 50 | 51 | /** 52 | * @brief Time delta constant for the tick-less mode. 53 | * @note If this value is zero then the system uses the classic 54 | * periodic tick. This value represents the minimum number 55 | * of ticks that is safe to specify in a timeout directive. 56 | * The value one is not valid, timeouts are rounded up to 57 | * this value. 58 | */ 59 | #define CH_CFG_ST_TIMEDELTA 2 60 | 61 | /** @} */ 62 | 63 | /*===========================================================================*/ 64 | /** 65 | * @name Kernel parameters and options 66 | * @{ 67 | */ 68 | /*===========================================================================*/ 69 | 70 | /** 71 | * @brief Round robin interval. 72 | * @details This constant is the number of system ticks allowed for the 73 | * threads before preemption occurs. Setting this value to zero 74 | * disables the preemption for threads with equal priority and the 75 | * round robin becomes cooperative. Note that higher priority 76 | * threads can still preempt, the kernel is always preemptive. 77 | * @note Disabling the round robin preemption makes the kernel more compact 78 | * and generally faster. 79 | * @note The round robin preemption is not supported in tickless mode and 80 | * must be set to zero in that case. 81 | */ 82 | #define CH_CFG_TIME_QUANTUM 0 83 | 84 | /** 85 | * @brief Managed RAM size. 86 | * @details Size of the RAM area to be managed by the OS. If set to zero 87 | * then the whole available RAM is used. The core memory is made 88 | * available to the heap allocator and/or can be used directly through 89 | * the simplified core memory allocator. 90 | * 91 | * @note In order to let the OS manage the whole RAM the linker script must 92 | * provide the @p __heap_base__ and @p __heap_end__ symbols. 93 | * @note Requires @p CH_CFG_USE_MEMCORE. 94 | */ 95 | #define CH_CFG_MEMCORE_SIZE 0 96 | 97 | /** 98 | * @brief Idle thread automatic spawn suppression. 99 | * @details When this option is activated the function @p chSysInit() 100 | * does not spawn the idle thread. The application @p main() 101 | * function becomes the idle thread and must implement an 102 | * infinite loop. 103 | */ 104 | #define CH_CFG_NO_IDLE_THREAD FALSE 105 | 106 | /** @} */ 107 | 108 | /*===========================================================================*/ 109 | /** 110 | * @name Performance options 111 | * @{ 112 | */ 113 | /*===========================================================================*/ 114 | 115 | /** 116 | * @brief OS optimization. 117 | * @details If enabled then time efficient rather than space efficient code 118 | * is used when two possible implementations exist. 119 | * 120 | * @note This is not related to the compiler optimization options. 121 | * @note The default is @p TRUE. 122 | */ 123 | #define CH_CFG_OPTIMIZE_SPEED TRUE 124 | 125 | /** @} */ 126 | 127 | /*===========================================================================*/ 128 | /** 129 | * @name Subsystem options 130 | * @{ 131 | */ 132 | /*===========================================================================*/ 133 | 134 | /** 135 | * @brief Time Measurement APIs. 136 | * @details If enabled then the time measurement APIs are included in 137 | * the kernel. 138 | * 139 | * @note The default is @p TRUE. 140 | */ 141 | #define CH_CFG_USE_TM TRUE 142 | 143 | /** 144 | * @brief Threads registry APIs. 145 | * @details If enabled then the registry APIs are included in the kernel. 146 | * 147 | * @note The default is @p TRUE. 148 | */ 149 | #define CH_CFG_USE_REGISTRY TRUE 150 | 151 | /** 152 | * @brief Threads synchronization APIs. 153 | * @details If enabled then the @p chThdWait() function is included in 154 | * the kernel. 155 | * 156 | * @note The default is @p TRUE. 157 | */ 158 | #define CH_CFG_USE_WAITEXIT TRUE 159 | 160 | /** 161 | * @brief Semaphores APIs. 162 | * @details If enabled then the Semaphores APIs are included in the kernel. 163 | * 164 | * @note The default is @p TRUE. 165 | */ 166 | #define CH_CFG_USE_SEMAPHORES TRUE 167 | 168 | /** 169 | * @brief Semaphores queuing mode. 170 | * @details If enabled then the threads are enqueued on semaphores by 171 | * priority rather than in FIFO order. 172 | * 173 | * @note The default is @p FALSE. Enable this if you have special 174 | * requirements. 175 | * @note Requires @p CH_CFG_USE_SEMAPHORES. 176 | */ 177 | #define CH_CFG_USE_SEMAPHORES_PRIORITY FALSE 178 | 179 | /** 180 | * @brief Mutexes APIs. 181 | * @details If enabled then the mutexes APIs are included in the kernel. 182 | * 183 | * @note The default is @p TRUE. 184 | */ 185 | #define CH_CFG_USE_MUTEXES TRUE 186 | 187 | /** 188 | * @brief Enables recursive behavior on mutexes. 189 | * @note Recursive mutexes are heavier and have an increased 190 | * memory footprint. 191 | * 192 | * @note The default is @p FALSE. 193 | * @note Requires @p CH_CFG_USE_MUTEXES. 194 | */ 195 | #define CH_CFG_USE_MUTEXES_RECURSIVE FALSE 196 | 197 | /** 198 | * @brief Conditional Variables APIs. 199 | * @details If enabled then the conditional variables APIs are included 200 | * in the kernel. 201 | * 202 | * @note The default is @p TRUE. 203 | * @note Requires @p CH_CFG_USE_MUTEXES. 204 | */ 205 | #define CH_CFG_USE_CONDVARS TRUE 206 | 207 | /** 208 | * @brief Conditional Variables APIs with timeout. 209 | * @details If enabled then the conditional variables APIs with timeout 210 | * specification are included in the kernel. 211 | * 212 | * @note The default is @p TRUE. 213 | * @note Requires @p CH_CFG_USE_CONDVARS. 214 | */ 215 | #define CH_CFG_USE_CONDVARS_TIMEOUT TRUE 216 | 217 | /** 218 | * @brief Events Flags APIs. 219 | * @details If enabled then the event flags APIs are included in the kernel. 220 | * 221 | * @note The default is @p TRUE. 222 | */ 223 | #define CH_CFG_USE_EVENTS TRUE 224 | 225 | /** 226 | * @brief Events Flags APIs with timeout. 227 | * @details If enabled then the events APIs with timeout specification 228 | * are included in the kernel. 229 | * 230 | * @note The default is @p TRUE. 231 | * @note Requires @p CH_CFG_USE_EVENTS. 232 | */ 233 | #define CH_CFG_USE_EVENTS_TIMEOUT TRUE 234 | 235 | /** 236 | * @brief Synchronous Messages APIs. 237 | * @details If enabled then the synchronous messages APIs are included 238 | * in the kernel. 239 | * 240 | * @note The default is @p TRUE. 241 | */ 242 | #define CH_CFG_USE_MESSAGES TRUE 243 | 244 | /** 245 | * @brief Synchronous Messages queuing mode. 246 | * @details If enabled then messages are served by priority rather than in 247 | * FIFO order. 248 | * 249 | * @note The default is @p FALSE. Enable this if you have special 250 | * requirements. 251 | * @note Requires @p CH_CFG_USE_MESSAGES. 252 | */ 253 | #define CH_CFG_USE_MESSAGES_PRIORITY FALSE 254 | 255 | /** 256 | * @brief Mailboxes APIs. 257 | * @details If enabled then the asynchronous messages (mailboxes) APIs are 258 | * included in the kernel. 259 | * 260 | * @note The default is @p TRUE. 261 | * @note Requires @p CH_CFG_USE_SEMAPHORES. 262 | */ 263 | #define CH_CFG_USE_MAILBOXES TRUE 264 | 265 | /** 266 | * @brief I/O Queues APIs. 267 | * @details If enabled then the I/O queues APIs are included in the kernel. 268 | * 269 | * @note The default is @p TRUE. 270 | */ 271 | #define CH_CFG_USE_QUEUES TRUE 272 | 273 | /** 274 | * @brief Core Memory Manager APIs. 275 | * @details If enabled then the core memory manager APIs are included 276 | * in the kernel. 277 | * 278 | * @note The default is @p TRUE. 279 | */ 280 | #define CH_CFG_USE_MEMCORE TRUE 281 | 282 | /** 283 | * @brief Heap Allocator APIs. 284 | * @details If enabled then the memory heap allocator APIs are included 285 | * in the kernel. 286 | * 287 | * @note The default is @p TRUE. 288 | * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or 289 | * @p CH_CFG_USE_SEMAPHORES. 290 | * @note Mutexes are recommended. 291 | */ 292 | #define CH_CFG_USE_HEAP TRUE 293 | 294 | /** 295 | * @brief Memory Pools Allocator APIs. 296 | * @details If enabled then the memory pools allocator APIs are included 297 | * in the kernel. 298 | * 299 | * @note The default is @p TRUE. 300 | */ 301 | #define CH_CFG_USE_MEMPOOLS TRUE 302 | 303 | /** 304 | * @brief Dynamic Threads APIs. 305 | * @details If enabled then the dynamic threads creation APIs are included 306 | * in the kernel. 307 | * 308 | * @note The default is @p TRUE. 309 | * @note Requires @p CH_CFG_USE_WAITEXIT. 310 | * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS. 311 | */ 312 | #define CH_CFG_USE_DYNAMIC TRUE 313 | 314 | /** @} */ 315 | 316 | /*===========================================================================*/ 317 | /** 318 | * @name Debug options 319 | * @{ 320 | */ 321 | /*===========================================================================*/ 322 | 323 | /** 324 | * @brief Debug option, kernel statistics. 325 | * 326 | * @note The default is @p FALSE. 327 | */ 328 | #define CH_DBG_STATISTICS FALSE 329 | 330 | /** 331 | * @brief Debug option, system state check. 332 | * @details If enabled the correct call protocol for system APIs is checked 333 | * at runtime. 334 | * 335 | * @note The default is @p FALSE. 336 | */ 337 | #define CH_DBG_SYSTEM_STATE_CHECK FALSE 338 | 339 | /** 340 | * @brief Debug option, parameters checks. 341 | * @details If enabled then the checks on the API functions input 342 | * parameters are activated. 343 | * 344 | * @note The default is @p FALSE. 345 | */ 346 | #define CH_DBG_ENABLE_CHECKS FALSE 347 | 348 | /** 349 | * @brief Debug option, consistency checks. 350 | * @details If enabled then all the assertions in the kernel code are 351 | * activated. This includes consistency checks inside the kernel, 352 | * runtime anomalies and port-defined checks. 353 | * 354 | * @note The default is @p FALSE. 355 | */ 356 | #define CH_DBG_ENABLE_ASSERTS FALSE 357 | 358 | /** 359 | * @brief Debug option, trace buffer. 360 | * @details If enabled then the context switch circular trace buffer is 361 | * activated. 362 | * 363 | * @note The default is @p FALSE. 364 | */ 365 | #define CH_DBG_ENABLE_TRACE FALSE 366 | 367 | /** 368 | * @brief Debug option, stack checks. 369 | * @details If enabled then a runtime stack check is performed. 370 | * 371 | * @note The default is @p FALSE. 372 | * @note The stack check is performed in a architecture/port dependent way. 373 | * It may not be implemented or some ports. 374 | * @note The default failure mode is to halt the system with the global 375 | * @p panic_msg variable set to @p NULL. 376 | */ 377 | #define CH_DBG_ENABLE_STACK_CHECK FALSE 378 | 379 | /** 380 | * @brief Debug option, stacks initialization. 381 | * @details If enabled then the threads working area is filled with a byte 382 | * value when a thread is created. This can be useful for the 383 | * runtime measurement of the used stack. 384 | * 385 | * @note The default is @p FALSE. 386 | */ 387 | #define CH_DBG_FILL_THREADS FALSE 388 | 389 | /** 390 | * @brief Debug option, threads profiling. 391 | * @details If enabled then a field is added to the @p thread_t structure that 392 | * counts the system ticks occurred while executing the thread. 393 | * 394 | * @note The default is @p FALSE. 395 | * @note This debug option is not currently compatible with the 396 | * tickless mode. 397 | */ 398 | #define CH_DBG_THREADS_PROFILING FALSE 399 | 400 | /** @} */ 401 | 402 | /*===========================================================================*/ 403 | /** 404 | * @name Kernel hooks 405 | * @{ 406 | */ 407 | /*===========================================================================*/ 408 | 409 | /** 410 | * @brief Threads descriptor structure extension. 411 | * @details User fields added to the end of the @p thread_t structure. 412 | */ 413 | #define CH_CFG_THREAD_EXTRA_FIELDS \ 414 | /* Add threads custom fields here.*/ 415 | 416 | /** 417 | * @brief Threads initialization hook. 418 | * @details User initialization code added to the @p chThdInit() API. 419 | * 420 | * @note It is invoked from within @p chThdInit() and implicitly from all 421 | * the threads creation APIs. 422 | */ 423 | #define CH_CFG_THREAD_INIT_HOOK(tp) { \ 424 | /* Add threads initialization code here.*/ \ 425 | } 426 | 427 | /** 428 | * @brief Threads finalization hook. 429 | * @details User finalization code added to the @p chThdExit() API. 430 | * 431 | * @note It is inserted into lock zone. 432 | * @note It is also invoked when the threads simply return in order to 433 | * terminate. 434 | */ 435 | #define CH_CFG_THREAD_EXIT_HOOK(tp) { \ 436 | /* Add threads finalization code here.*/ \ 437 | } 438 | 439 | /** 440 | * @brief Context switch hook. 441 | * @details This hook is invoked just before switching between threads. 442 | */ 443 | #define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \ 444 | /* Context switch code here.*/ \ 445 | } 446 | 447 | /** 448 | * @brief Idle thread enter hook. 449 | * @note This hook is invoked within a critical zone, no OS functions 450 | * should be invoked from here. 451 | * @note This macro can be used to activate a power saving mode. 452 | */ 453 | #define CH_CFG_IDLE_ENTER_HOOK() { \ 454 | } 455 | 456 | /** 457 | * @brief Idle thread leave hook. 458 | * @note This hook is invoked within a critical zone, no OS functions 459 | * should be invoked from here. 460 | * @note This macro can be used to deactivate a power saving mode. 461 | */ 462 | #define CH_CFG_IDLE_LEAVE_HOOK() { \ 463 | } 464 | 465 | /** 466 | * @brief Idle Loop hook. 467 | * @details This hook is continuously invoked by the idle thread loop. 468 | */ 469 | #define CH_CFG_IDLE_LOOP_HOOK() { \ 470 | /* Idle loop code here.*/ \ 471 | } 472 | 473 | /** 474 | * @brief System tick event hook. 475 | * @details This hook is invoked in the system tick handler immediately 476 | * after processing the virtual timers queue. 477 | */ 478 | #define CH_CFG_SYSTEM_TICK_HOOK() { \ 479 | /* System tick event code here.*/ \ 480 | } 481 | 482 | /** 483 | * @brief System halt hook. 484 | * @details This hook is invoked in case to a system halting error before 485 | * the system is halted. 486 | */ 487 | #define CH_CFG_SYSTEM_HALT_HOOK(reason) { \ 488 | /* System halt code here.*/ \ 489 | } 490 | 491 | /** @} */ 492 | 493 | /*===========================================================================*/ 494 | /* Port-specific settings (override port settings defaulted in chcore.h). */ 495 | /*===========================================================================*/ 496 | 497 | #endif /* _CHCONF_H_ */ 498 | 499 | /** @} */ 500 | -------------------------------------------------------------------------------- /examples/chibios/STM32F302R8-Nucleo/halconf.h: -------------------------------------------------------------------------------- 1 | /* 2 | ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /** 18 | * @file templates/halconf.h 19 | * @brief HAL configuration header. 20 | * @details HAL configuration file, this file allows to enable or disable the 21 | * various device drivers from your application. You may also use 22 | * this file in order to override the device drivers default settings. 23 | * 24 | * @addtogroup HAL_CONF 25 | * @{ 26 | */ 27 | 28 | #ifndef _HALCONF_H_ 29 | #define _HALCONF_H_ 30 | 31 | #include "mcuconf.h" 32 | 33 | /** 34 | * @brief Enables the PAL subsystem. 35 | */ 36 | #if !defined(HAL_USE_PAL) || defined(__DOXYGEN__) 37 | #define HAL_USE_PAL TRUE 38 | #endif 39 | 40 | /** 41 | * @brief Enables the ADC subsystem. 42 | */ 43 | #if !defined(HAL_USE_ADC) || defined(__DOXYGEN__) 44 | #define HAL_USE_ADC FALSE 45 | #endif 46 | 47 | /** 48 | * @brief Enables the CAN subsystem. 49 | */ 50 | #if !defined(HAL_USE_CAN) || defined(__DOXYGEN__) 51 | #define HAL_USE_CAN FALSE 52 | #endif 53 | 54 | /** 55 | * @brief Enables the DAC subsystem. 56 | */ 57 | #if !defined(HAL_USE_DAC) || defined(__DOXYGEN__) 58 | #define HAL_USE_DAC FALSE 59 | #endif 60 | 61 | /** 62 | * @brief Enables the EXT subsystem. 63 | */ 64 | #if !defined(HAL_USE_EXT) || defined(__DOXYGEN__) 65 | #define HAL_USE_EXT FALSE 66 | #endif 67 | 68 | /** 69 | * @brief Enables the GPT subsystem. 70 | */ 71 | #if !defined(HAL_USE_GPT) || defined(__DOXYGEN__) 72 | #define HAL_USE_GPT FALSE 73 | #endif 74 | 75 | /** 76 | * @brief Enables the I2C subsystem. 77 | */ 78 | #if !defined(HAL_USE_I2C) || defined(__DOXYGEN__) 79 | #define HAL_USE_I2C FALSE 80 | #endif 81 | 82 | /** 83 | * @brief Enables the I2S subsystem. 84 | */ 85 | #if !defined(HAL_USE_I2S) || defined(__DOXYGEN__) 86 | #define HAL_USE_I2S FALSE 87 | #endif 88 | 89 | /** 90 | * @brief Enables the ICU subsystem. 91 | */ 92 | #if !defined(HAL_USE_ICU) || defined(__DOXYGEN__) 93 | #define HAL_USE_ICU FALSE 94 | #endif 95 | 96 | /** 97 | * @brief Enables the MAC subsystem. 98 | */ 99 | #if !defined(HAL_USE_MAC) || defined(__DOXYGEN__) 100 | #define HAL_USE_MAC FALSE 101 | #endif 102 | 103 | /** 104 | * @brief Enables the MMC_SPI subsystem. 105 | */ 106 | #if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__) 107 | #define HAL_USE_MMC_SPI FALSE 108 | #endif 109 | 110 | /** 111 | * @brief Enables the PWM subsystem. 112 | */ 113 | #if !defined(HAL_USE_PWM) || defined(__DOXYGEN__) 114 | #define HAL_USE_PWM FALSE 115 | #endif 116 | 117 | /** 118 | * @brief Enables the RTC subsystem. 119 | */ 120 | #if !defined(HAL_USE_RTC) || defined(__DOXYGEN__) 121 | #define HAL_USE_RTC FALSE 122 | #endif 123 | 124 | /** 125 | * @brief Enables the SDC subsystem. 126 | */ 127 | #if !defined(HAL_USE_SDC) || defined(__DOXYGEN__) 128 | #define HAL_USE_SDC FALSE 129 | #endif 130 | 131 | /** 132 | * @brief Enables the SERIAL subsystem. 133 | */ 134 | #if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__) 135 | #define HAL_USE_SERIAL TRUE 136 | #endif 137 | 138 | /** 139 | * @brief Enables the SERIAL over USB subsystem. 140 | */ 141 | #if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__) 142 | #define HAL_USE_SERIAL_USB FALSE 143 | #endif 144 | 145 | /** 146 | * @brief Enables the SPI subsystem. 147 | */ 148 | #if !defined(HAL_USE_SPI) || defined(__DOXYGEN__) 149 | #define HAL_USE_SPI FALSE 150 | #endif 151 | 152 | /** 153 | * @brief Enables the UART subsystem. 154 | */ 155 | #if !defined(HAL_USE_UART) || defined(__DOXYGEN__) 156 | #define HAL_USE_UART FALSE 157 | #endif 158 | 159 | /** 160 | * @brief Enables the USB subsystem. 161 | */ 162 | #if !defined(HAL_USE_USB) || defined(__DOXYGEN__) 163 | #define HAL_USE_USB FALSE 164 | #endif 165 | 166 | /*===========================================================================*/ 167 | /* ADC driver related settings. */ 168 | /*===========================================================================*/ 169 | 170 | /** 171 | * @brief Enables synchronous APIs. 172 | * @note Disabling this option saves both code and data space. 173 | */ 174 | #if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__) 175 | #define ADC_USE_WAIT TRUE 176 | #endif 177 | 178 | /** 179 | * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs. 180 | * @note Disabling this option saves both code and data space. 181 | */ 182 | #if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) 183 | #define ADC_USE_MUTUAL_EXCLUSION TRUE 184 | #endif 185 | 186 | /*===========================================================================*/ 187 | /* CAN driver related settings. */ 188 | /*===========================================================================*/ 189 | 190 | /** 191 | * @brief Sleep mode related APIs inclusion switch. 192 | */ 193 | #if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__) 194 | #define CAN_USE_SLEEP_MODE TRUE 195 | #endif 196 | 197 | /*===========================================================================*/ 198 | /* I2C driver related settings. */ 199 | /*===========================================================================*/ 200 | 201 | /** 202 | * @brief Enables the mutual exclusion APIs on the I2C bus. 203 | */ 204 | #if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) 205 | #define I2C_USE_MUTUAL_EXCLUSION TRUE 206 | #endif 207 | 208 | /*===========================================================================*/ 209 | /* MAC driver related settings. */ 210 | /*===========================================================================*/ 211 | 212 | /** 213 | * @brief Enables an event sources for incoming packets. 214 | */ 215 | #if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__) 216 | #define MAC_USE_ZERO_COPY FALSE 217 | #endif 218 | 219 | /** 220 | * @brief Enables an event sources for incoming packets. 221 | */ 222 | #if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__) 223 | #define MAC_USE_EVENTS TRUE 224 | #endif 225 | 226 | /*===========================================================================*/ 227 | /* MMC_SPI driver related settings. */ 228 | /*===========================================================================*/ 229 | 230 | /** 231 | * @brief Delays insertions. 232 | * @details If enabled this options inserts delays into the MMC waiting 233 | * routines releasing some extra CPU time for the threads with 234 | * lower priority, this may slow down the driver a bit however. 235 | * This option is recommended also if the SPI driver does not 236 | * use a DMA channel and heavily loads the CPU. 237 | */ 238 | #if !defined(MMC_NICE_WAITING) || defined(__DOXYGEN__) 239 | #define MMC_NICE_WAITING TRUE 240 | #endif 241 | 242 | /*===========================================================================*/ 243 | /* SDC driver related settings. */ 244 | /*===========================================================================*/ 245 | 246 | /** 247 | * @brief Number of initialization attempts before rejecting the card. 248 | * @note Attempts are performed at 10mS intervals. 249 | */ 250 | #if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__) 251 | #define SDC_INIT_RETRY 100 252 | #endif 253 | 254 | /** 255 | * @brief Include support for MMC cards. 256 | * @note MMC support is not yet implemented so this option must be kept 257 | * at @p FALSE. 258 | */ 259 | #if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__) 260 | #define SDC_MMC_SUPPORT FALSE 261 | #endif 262 | 263 | /** 264 | * @brief Delays insertions. 265 | * @details If enabled this options inserts delays into the MMC waiting 266 | * routines releasing some extra CPU time for the threads with 267 | * lower priority, this may slow down the driver a bit however. 268 | */ 269 | #if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__) 270 | #define SDC_NICE_WAITING TRUE 271 | #endif 272 | 273 | /*===========================================================================*/ 274 | /* SERIAL driver related settings. */ 275 | /*===========================================================================*/ 276 | 277 | /** 278 | * @brief Default bit rate. 279 | * @details Configuration parameter, this is the baud rate selected for the 280 | * default configuration. 281 | */ 282 | #if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__) 283 | #define SERIAL_DEFAULT_BITRATE 38400 284 | #endif 285 | 286 | /** 287 | * @brief Serial buffers size. 288 | * @details Configuration parameter, you can change the depth of the queue 289 | * buffers depending on the requirements of your application. 290 | * @note The default is 64 bytes for both the transmission and receive 291 | * buffers. 292 | */ 293 | #if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__) 294 | #define SERIAL_BUFFERS_SIZE 16 295 | #endif 296 | 297 | /*===========================================================================*/ 298 | /* SERIAL_USB driver related setting. */ 299 | /*===========================================================================*/ 300 | 301 | /** 302 | * @brief Serial over USB buffers size. 303 | * @details Configuration parameter, the buffer size must be a multiple of 304 | * the USB data endpoint maximum packet size. 305 | * @note The default is 64 bytes for both the transmission and receive 306 | * buffers. 307 | */ 308 | #if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__) 309 | #define SERIAL_USB_BUFFERS_SIZE 256 310 | #endif 311 | 312 | /*===========================================================================*/ 313 | /* SPI driver related settings. */ 314 | /*===========================================================================*/ 315 | 316 | /** 317 | * @brief Enables synchronous APIs. 318 | * @note Disabling this option saves both code and data space. 319 | */ 320 | #if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__) 321 | #define SPI_USE_WAIT TRUE 322 | #endif 323 | 324 | /** 325 | * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs. 326 | * @note Disabling this option saves both code and data space. 327 | */ 328 | #if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) 329 | #define SPI_USE_MUTUAL_EXCLUSION TRUE 330 | #endif 331 | 332 | #endif /* _HALCONF_H_ */ 333 | 334 | /** @} */ 335 | -------------------------------------------------------------------------------- /examples/chibios/STM32F302R8-Nucleo/main.c: -------------------------------------------------------------------------------- 1 | #include "ch.h" 2 | #include "hal.h" 3 | #include "../../../messagebus.h" 4 | #include 5 | 6 | 7 | messagebus_t bus; 8 | MUTEX_DECL(bus_lock); 9 | CONDVAR_DECL(bus_condvar); 10 | 11 | 12 | int send_command(int command, void *message) 13 | { 14 | int ret; 15 | asm volatile("mov r0, %[cmd];" 16 | "mov r1, %[msg];" 17 | "bkpt #0xAB;" 18 | "mov %[ret], r0;" 19 | : [ret] "=r" (ret) 20 | : [cmd] "r" (command), [msg] "r" (message) 21 | : "r0", "r1", "memory"); 22 | return ret; 23 | } 24 | 25 | void print(const char *string) 26 | { 27 | 28 | uint32_t m[] = {1/*stdout*/, (uint32_t)string, strlen(string)}; 29 | send_command(0x05, m); 30 | } 31 | 32 | static THD_FUNCTION(button_thread, arg) 33 | { 34 | 35 | (void)arg; 36 | chRegSetThreadName(__FUNCTION__); 37 | 38 | messagebus_topic_t *button_topic; 39 | button_topic = messagebus_find_topic_blocking(&bus, "/button_pressed"); 40 | 41 | while (true) { 42 | while (palReadPad(GPIOC, GPIOC_BUTTON)) { 43 | chThdSleepMilliseconds(100); 44 | } 45 | messagebus_topic_publish(button_topic, NULL, 0); 46 | chThdSleepMilliseconds(100); 47 | while (!palReadPad(GPIOC, GPIOC_BUTTON)) { 48 | chThdSleepMilliseconds(100); 49 | } 50 | } 51 | } 52 | 53 | static THD_FUNCTION(led_thread, arg) 54 | { 55 | (void)arg; 56 | chRegSetThreadName(__FUNCTION__); 57 | messagebus_topic_t *button_topic; 58 | 59 | button_topic = messagebus_find_topic_blocking(&bus, "/button_pressed"); 60 | while (true) { 61 | messagebus_topic_wait(button_topic, NULL, 0); 62 | palTogglePad(GPIOB, GPIOB_LED_GREEN); 63 | } 64 | } 65 | 66 | static THD_FUNCTION(console_thread, arg) 67 | { 68 | (void)arg; 69 | chRegSetThreadName(__FUNCTION__); 70 | messagebus_topic_t *button_topic; 71 | 72 | print("waiting for topic\n"); 73 | button_topic = messagebus_find_topic_blocking(&bus, "/button_pressed"); 74 | print("done\n"); 75 | while (true) { 76 | messagebus_topic_wait(button_topic, NULL, 0); 77 | print("button pressed\r\n"); 78 | } 79 | } 80 | 81 | int main(void) { 82 | halInit(); 83 | chSysInit(); 84 | print("==boot=="); 85 | messagebus_init(&bus, &bus_lock, &bus_condvar); 86 | 87 | messagebus_topic_t button_topic; 88 | MUTEX_DECL(button_topic_lock); 89 | CONDVAR_DECL(button_topic_condvar); 90 | 91 | messagebus_topic_init(&button_topic, 92 | &button_topic_lock, &button_topic_condvar, 93 | NULL, 0); 94 | 95 | messagebus_advertise_topic(&bus, &button_topic, "/button_pressed"); 96 | 97 | static THD_WORKING_AREA(button_thread_wa, 128); 98 | chThdCreateStatic(button_thread_wa, sizeof(button_thread_wa), 99 | NORMALPRIO, button_thread, NULL); 100 | 101 | static THD_WORKING_AREA(led_thread_wa, 128); 102 | chThdCreateStatic(led_thread_wa, sizeof(led_thread_wa), 103 | NORMALPRIO, led_thread, NULL); 104 | 105 | static THD_WORKING_AREA(console_thread_wa, 128); 106 | chThdCreateStatic(console_thread_wa, sizeof(console_thread_wa), 107 | NORMALPRIO+1, console_thread, NULL); 108 | 109 | while (true) { 110 | chThdSleepMilliseconds(500); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /examples/chibios/STM32F302R8-Nucleo/mcuconf.h: -------------------------------------------------------------------------------- 1 | /* 2 | ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef _MCUCONF_H_ 18 | #define _MCUCONF_H_ 19 | 20 | /* 21 | * STM32F3xx drivers configuration. 22 | * The following settings override the default settings present in 23 | * the various device driver implementation headers. 24 | * Note that the settings for each driver only have effect if the whole 25 | * driver is enabled in halconf.h. 26 | * 27 | * IRQ priorities: 28 | * 15...0 Lowest...Highest. 29 | * 30 | * DMA priorities: 31 | * 0...3 Lowest...Highest. 32 | */ 33 | 34 | #define STM32F3xx_MCUCONF 35 | 36 | /* 37 | * HAL driver system settings. 38 | */ 39 | #define STM32_NO_INIT FALSE 40 | #define STM32_PVD_ENABLE FALSE 41 | #define STM32_PLS STM32_PLS_LEV0 42 | #define STM32_HSI_ENABLED TRUE 43 | #define STM32_LSI_ENABLED TRUE 44 | #define STM32_HSE_ENABLED FALSE 45 | #define STM32_LSE_ENABLED FALSE 46 | #define STM32_SW STM32_SW_PLL 47 | #define STM32_PLLSRC STM32_PLLSRC_HSI 48 | #define STM32_PREDIV_VALUE 1 49 | #define STM32_PLLMUL_VALUE 12 50 | #define STM32_HPRE STM32_HPRE_DIV1 51 | #define STM32_PPRE1 STM32_PPRE1_DIV2 52 | #define STM32_PPRE2 STM32_PPRE2_DIV2 53 | #define STM32_MCOSEL STM32_MCOSEL_NOCLOCK 54 | #define STM32_ADC12PRES STM32_ADC12PRES_DIV1 55 | #define STM32_USART1SW STM32_USART1SW_PCLK 56 | #define STM32_USART2SW STM32_USART2SW_PCLK 57 | #define STM32_USART3SW STM32_USART3SW_PCLK 58 | #define STM32_I2C1SW STM32_I2C1SW_SYSCLK 59 | #define STM32_TIM1SW STM32_TIM1SW_PCLK2 60 | #define STM32_RTCSEL STM32_RTCSEL_LSI 61 | 62 | /* 63 | * ADC driver system settings. 64 | */ 65 | #define STM32_ADC_USE_ADC1 FALSE 66 | #define STM32_ADC_ADC12_DMA_PRIORITY 2 67 | #define STM32_ADC_ADC12_IRQ_PRIORITY 5 68 | #define STM32_ADC_ADC12_DMA_IRQ_PRIORITY 5 69 | #define STM32_ADC_ADC12_CLOCK_MODE ADC_CCR_CKMODE_AHB_DIV1 70 | #define STM32_ADC_DUAL_MODE FALSE 71 | 72 | /* 73 | * CAN driver system settings. 74 | */ 75 | #define STM32_CAN_USE_CAN1 FALSE 76 | #define STM32_CAN_CAN1_IRQ_PRIORITY 11 77 | 78 | /* 79 | * EXT driver system settings. 80 | */ 81 | #define STM32_EXT_EXTI0_IRQ_PRIORITY 6 82 | #define STM32_EXT_EXTI1_IRQ_PRIORITY 6 83 | #define STM32_EXT_EXTI2_IRQ_PRIORITY 6 84 | #define STM32_EXT_EXTI3_IRQ_PRIORITY 6 85 | #define STM32_EXT_EXTI4_IRQ_PRIORITY 6 86 | #define STM32_EXT_EXTI5_9_IRQ_PRIORITY 6 87 | #define STM32_EXT_EXTI10_15_IRQ_PRIORITY 6 88 | #define STM32_EXT_EXTI16_IRQ_PRIORITY 6 89 | #define STM32_EXT_EXTI17_IRQ_PRIORITY 6 90 | #define STM32_EXT_EXTI18_IRQ_PRIORITY 6 91 | #define STM32_EXT_EXTI19_IRQ_PRIORITY 6 92 | #define STM32_EXT_EXTI20_IRQ_PRIORITY 6 93 | #define STM32_EXT_EXTI21_22_29_IRQ_PRIORITY 6 94 | #define STM32_EXT_EXTI30_32_IRQ_PRIORITY 6 95 | #define STM32_EXT_EXTI33_IRQ_PRIORITY 6 96 | 97 | /* 98 | * GPT driver system settings. 99 | */ 100 | #define STM32_GPT_USE_TIM1 FALSE 101 | #define STM32_GPT_USE_TIM2 FALSE 102 | #define STM32_GPT_USE_TIM3 FALSE 103 | #define STM32_GPT_USE_TIM6 FALSE 104 | #define STM32_GPT_USE_TIM7 FALSE 105 | #define STM32_GPT_TIM1_IRQ_PRIORITY 7 106 | #define STM32_GPT_TIM2_IRQ_PRIORITY 7 107 | #define STM32_GPT_TIM3_IRQ_PRIORITY 7 108 | #define STM32_GPT_TIM6_IRQ_PRIORITY 7 109 | #define STM32_GPT_TIM7_IRQ_PRIORITY 7 110 | 111 | /* 112 | * I2C driver system settings. 113 | */ 114 | #define STM32_I2C_USE_I2C1 FALSE 115 | #define STM32_I2C_BUSY_TIMEOUT 50 116 | #define STM32_I2C_I2C1_IRQ_PRIORITY 10 117 | #define STM32_I2C_USE_DMA TRUE 118 | #define STM32_I2C_I2C1_DMA_PRIORITY 1 119 | #define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure") 120 | 121 | /* 122 | * ICU driver system settings. 123 | */ 124 | #define STM32_ICU_USE_TIM1 FALSE 125 | #define STM32_ICU_USE_TIM2 FALSE 126 | #define STM32_ICU_USE_TIM3 FALSE 127 | #define STM32_ICU_TIM1_IRQ_PRIORITY 7 128 | #define STM32_ICU_TIM2_IRQ_PRIORITY 7 129 | #define STM32_ICU_TIM3_IRQ_PRIORITY 7 130 | 131 | /* 132 | * PWM driver system settings. 133 | */ 134 | #define STM32_PWM_USE_ADVANCED FALSE 135 | #define STM32_PWM_USE_TIM1 FALSE 136 | #define STM32_PWM_USE_TIM2 FALSE 137 | #define STM32_PWM_USE_TIM3 FALSE 138 | #define STM32_PWM_TIM1_IRQ_PRIORITY 7 139 | #define STM32_PWM_TIM2_IRQ_PRIORITY 7 140 | #define STM32_PWM_TIM3_IRQ_PRIORITY 7 141 | 142 | /* 143 | * SERIAL driver system settings. 144 | */ 145 | #define STM32_SERIAL_USE_USART1 FALSE 146 | #define STM32_SERIAL_USE_USART2 TRUE 147 | #define STM32_SERIAL_USE_USART3 FALSE 148 | #define STM32_SERIAL_USART1_PRIORITY 12 149 | #define STM32_SERIAL_USART2_PRIORITY 12 150 | #define STM32_SERIAL_USART3_PRIORITY 12 151 | 152 | /* 153 | * SPI driver system settings. 154 | */ 155 | #define STM32_SPI_USE_SPI1 FALSE 156 | #define STM32_SPI_SPI1_DMA_PRIORITY 1 157 | #define STM32_SPI_SPI1_IRQ_PRIORITY 10 158 | #define STM32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure") 159 | 160 | /* 161 | * ST driver system settings. 162 | */ 163 | #define STM32_ST_IRQ_PRIORITY 8 164 | #define STM32_ST_USE_TIMER 2 165 | 166 | /* 167 | * UART driver system settings. 168 | */ 169 | #define STM32_UART_USE_USART1 FALSE 170 | #define STM32_UART_USE_USART2 FALSE 171 | #define STM32_UART_USE_USART3 FALSE 172 | #define STM32_UART_USART1_IRQ_PRIORITY 12 173 | #define STM32_UART_USART2_IRQ_PRIORITY 12 174 | #define STM32_UART_USART3_IRQ_PRIORITY 12 175 | #define STM32_UART_USART1_DMA_PRIORITY 0 176 | #define STM32_UART_USART2_DMA_PRIORITY 0 177 | #define STM32_UART_USART3_DMA_PRIORITY 0 178 | #define STM32_UART_DMA_ERROR_HOOK(uartp) osalSysHalt("DMA failure") 179 | 180 | #endif /* _MCUCONF_H_ */ 181 | -------------------------------------------------------------------------------- /examples/chibios/STM32F302R8-Nucleo/openocd.cfg: -------------------------------------------------------------------------------- 1 | telnet_port 4444 2 | gdb_port 3333 3 | 4 | source [find board/st_nucleo_f3.cfg] 5 | -------------------------------------------------------------------------------- /examples/chibios/port.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../../messagebus.h" 3 | 4 | void messagebus_lock_acquire(void *p) 5 | { 6 | mutex_t *lock = (mutex_t *)p; 7 | chMtxLock(lock); 8 | } 9 | 10 | void messagebus_lock_release(void *p) 11 | { 12 | mutex_t *lock = (mutex_t *)p; 13 | chMtxUnlock(lock); 14 | } 15 | 16 | void messagebus_condvar_broadcast(void *p) 17 | { 18 | condition_variable_t *cond = (condition_variable_t *)p; 19 | chCondBroadcast(cond); 20 | } 21 | 22 | void messagebus_condvar_wait(void *p) 23 | { 24 | condition_variable_t *cond = (condition_variable_t *)p; 25 | chCondWait(cond); 26 | } 27 | -------------------------------------------------------------------------------- /examples/posix/demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "../../messagebus.h" 6 | #include "port.h" 7 | 8 | messagebus_t bus; 9 | 10 | static void* producer(void *p) 11 | { 12 | messagebus_topic_t *topic; 13 | int counter = 0; 14 | int producer_number = (int)p; 15 | 16 | printf("[publisher %d] waiting for topic myint\n", producer_number); 17 | 18 | while (1) { 19 | topic = messagebus_find_topic_blocking(&bus, "myint"); 20 | printf("[publisher %d] writing %d on topic %s\n", 21 | producer_number, counter, topic->name); 22 | messagebus_topic_publish(topic, &counter, sizeof counter); 23 | counter += 1; 24 | sleep(2); 25 | } 26 | 27 | return NULL; 28 | } 29 | 30 | static void *consumer(void *p) 31 | { 32 | messagebus_topic_t *topic; 33 | int received; 34 | int consumer_number = (int)p; 35 | 36 | printf("[consumer %d] waiting for topic myint\n", consumer_number); 37 | 38 | while (1) { 39 | topic = messagebus_find_topic_blocking(&bus, "myint"); 40 | 41 | messagebus_topic_wait(topic, &received, sizeof received); 42 | printf("[consumer %d] read %d on topic %s\n", 43 | consumer_number, received, topic->name); 44 | } 45 | 46 | return NULL; 47 | } 48 | 49 | int main(int argc, const char **argv) 50 | { 51 | (void) argc; 52 | (void) argv; 53 | 54 | /* Create the message bus. */ 55 | condvar_wrapper_t bus_sync = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER}; 56 | messagebus_init(&bus, &bus_sync, &bus_sync); 57 | 58 | /* Creates a topic and publish it on the bus. */ 59 | messagebus_topic_t topic; 60 | int buffer; 61 | 62 | condvar_wrapper_t wrapper = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER}; 63 | messagebus_topic_init(&topic, &wrapper, &wrapper, &buffer, sizeof buffer); 64 | 65 | 66 | /* Creates a few consumer threads. */ 67 | pthread_t producer_thd, consumer_thd; 68 | pthread_create(&consumer_thd, NULL, consumer, (void *)1); 69 | pthread_create(&consumer_thd, NULL, consumer, (void *)2); 70 | pthread_create(&consumer_thd, NULL, consumer, (void *)3); 71 | 72 | /* Creates the producer threads, slightly offset */ 73 | pthread_create(&producer_thd, NULL, producer, (void *)1); 74 | 75 | sleep(1); 76 | messagebus_advertise_topic(&bus, &topic, "myint"); 77 | sleep(3); 78 | pthread_create(&producer_thd, NULL, producer, (void *)2); 79 | 80 | while(1) { 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /examples/posix/demo_watchgroups.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../../messagebus.h" 7 | #include "port.h" 8 | 9 | messagebus_t bus; 10 | 11 | static void* producer(void *p) 12 | { 13 | messagebus_topic_t *topic; 14 | int counter = 0; 15 | 16 | char *topic_name = (char *)p; 17 | 18 | printf("[publisher] waiting for topic \"%s\"\n", topic_name); 19 | 20 | while (1) { 21 | topic = messagebus_find_topic_blocking(&bus, topic_name); 22 | printf("[publisher] writing %d on topic %s\n", counter, topic->name); 23 | messagebus_topic_publish(topic, &counter, sizeof counter); 24 | counter += 1; 25 | sleep(2); 26 | } 27 | 28 | return NULL; 29 | } 30 | 31 | static void *observer(void *p) 32 | { 33 | messagebus_watchgroup_t group; 34 | 35 | condvar_wrapper_t wrapper = {PTHREAD_MUTEX_INITIALIZER, 36 | PTHREAD_COND_INITIALIZER}; 37 | 38 | messagebus_watcher_t watchers[2]; 39 | messagebus_watchgroup_init(&group, &wrapper, &wrapper); 40 | messagebus_watchgroup_watch(&watchers[0], 41 | &group, 42 | messagebus_find_topic_blocking(&bus, "foo")); 43 | messagebus_watchgroup_watch(&watchers[1], 44 | &group, 45 | messagebus_find_topic_blocking(&bus, "bar")); 46 | 47 | while (1) { 48 | messagebus_topic_t *topic; 49 | topic = messagebus_watchgroup_wait(&group); 50 | printf("[observer] Received a message of size %ld on \"%s\"\n", 51 | topic->buffer_len, 52 | topic->name); 53 | } 54 | } 55 | 56 | static void create_topic(const char *name) 57 | { 58 | messagebus_topic_t *topic = malloc(sizeof(messagebus_topic_t)); 59 | int *buffer = malloc(sizeof(int)); 60 | condvar_wrapper_t *sync = malloc(sizeof(condvar_wrapper_t)); 61 | pthread_mutex_init(&sync->mutex, NULL); 62 | pthread_cond_init(&sync->cond, NULL); 63 | 64 | messagebus_topic_init(topic, sync, sync, buffer, sizeof(int)); 65 | messagebus_advertise_topic(&bus, topic, name); 66 | } 67 | 68 | int main(int argc, const char **argv) 69 | { 70 | (void) argc; 71 | (void) argv; 72 | 73 | /* Create the message bus. */ 74 | condvar_wrapper_t bus_sync = {PTHREAD_MUTEX_INITIALIZER, 75 | PTHREAD_COND_INITIALIZER}; 76 | messagebus_init(&bus, &bus_sync, &bus_sync); 77 | 78 | /* Creates a topic and publish it on the bus. */ 79 | create_topic("foo"); 80 | create_topic("bar"); 81 | 82 | /* Create publishers */ 83 | pthread_t producer_foo_thd, producer_bar_thd; 84 | pthread_create(&producer_foo_thd, NULL, producer, "foo"); 85 | sleep(1); 86 | pthread_create(&producer_bar_thd, NULL, producer, "bar"); 87 | 88 | /* Observer thread */ 89 | pthread_t observer_thd; 90 | pthread_create(&observer_thd, NULL, observer, NULL); 91 | 92 | while (1) { 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /examples/posix/port.c: -------------------------------------------------------------------------------- 1 | #include "port.h" 2 | 3 | #include "../../messagebus.h" 4 | 5 | void messagebus_lock_acquire(void *p) 6 | { 7 | condvar_wrapper_t *wrapper = (condvar_wrapper_t *)p; 8 | pthread_mutex_lock(&wrapper->mutex); 9 | } 10 | 11 | void messagebus_lock_release(void *p) 12 | { 13 | condvar_wrapper_t *wrapper = (condvar_wrapper_t *)p; 14 | pthread_mutex_unlock(&wrapper->mutex); 15 | } 16 | 17 | void messagebus_condvar_broadcast(void *p) 18 | { 19 | condvar_wrapper_t *wrapper = (condvar_wrapper_t *)p; 20 | pthread_cond_broadcast(&wrapper->cond); 21 | } 22 | 23 | void messagebus_condvar_wait(void *p) 24 | { 25 | condvar_wrapper_t *wrapper = (condvar_wrapper_t *)p; 26 | pthread_cond_wait(&wrapper->cond, &wrapper->mutex); 27 | } 28 | -------------------------------------------------------------------------------- /examples/posix/port.h: -------------------------------------------------------------------------------- 1 | #ifndef PORT_H 2 | #define PORT_H 3 | #include 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | typedef struct { 10 | pthread_mutex_t mutex; 11 | pthread_cond_t cond; 12 | } condvar_wrapper_t; 13 | 14 | #ifdef __cplusplus 15 | } 16 | #endif 17 | #endif 18 | -------------------------------------------------------------------------------- /messagebus.c: -------------------------------------------------------------------------------- 1 | #include "messagebus.h" 2 | #include 3 | 4 | static messagebus_topic_t *topic_by_name(messagebus_t *bus, const char *name) 5 | { 6 | messagebus_topic_t *t; 7 | for (t = bus->topics.head; t != NULL; t = t->next) { 8 | if (!strcmp(name, t->name)) { 9 | return t; 10 | } 11 | } 12 | 13 | return NULL; 14 | } 15 | 16 | void messagebus_init(messagebus_t *bus, void *lock, void *condvar) 17 | { 18 | memset(bus, 0, sizeof(messagebus_t)); 19 | bus->lock = lock; 20 | bus->condvar = condvar; 21 | } 22 | 23 | void messagebus_topic_init(messagebus_topic_t *topic, void *topic_lock, void *topic_condvar, 24 | void *buffer, size_t buffer_len) 25 | { 26 | memset(topic, 0, sizeof(messagebus_topic_t)); 27 | topic->buffer = buffer; 28 | topic->buffer_len = buffer_len; 29 | topic->lock = topic_lock; 30 | topic->condvar = topic_condvar; 31 | } 32 | 33 | void messagebus_advertise_topic(messagebus_t *bus, messagebus_topic_t *topic, const char *name) 34 | { 35 | memset(topic->name, 0, sizeof(topic->name)); 36 | strncpy(topic->name, name, TOPIC_NAME_MAX_LENGTH); 37 | 38 | messagebus_lock_acquire(bus->lock); 39 | 40 | if (bus->topics.head != NULL) { 41 | topic->next = bus->topics.head; 42 | } 43 | bus->topics.head = topic; 44 | 45 | for (messagebus_new_topic_cb_t *cb = bus->new_topic_callback_list; cb != NULL; cb = cb->next) { 46 | cb->callback(bus, topic, cb->callback_arg); 47 | } 48 | 49 | messagebus_condvar_broadcast(bus->condvar); 50 | 51 | messagebus_lock_release(bus->lock); 52 | } 53 | 54 | messagebus_topic_t *messagebus_find_topic(messagebus_t *bus, const char *name) 55 | { 56 | messagebus_topic_t *res; 57 | 58 | messagebus_lock_acquire(bus->lock); 59 | 60 | res = topic_by_name(bus, name); 61 | 62 | messagebus_lock_release(bus->lock); 63 | 64 | return res; 65 | } 66 | 67 | messagebus_topic_t *messagebus_find_topic_blocking(messagebus_t *bus, const char *name) 68 | { 69 | messagebus_topic_t *res = NULL; 70 | 71 | messagebus_lock_acquire(bus->lock); 72 | 73 | while (res == NULL) { 74 | res = topic_by_name(bus, name); 75 | 76 | if (res == NULL) { 77 | messagebus_condvar_wait(bus->condvar); 78 | } 79 | } 80 | 81 | messagebus_lock_release(bus->lock); 82 | 83 | return res; 84 | } 85 | 86 | bool messagebus_topic_publish(messagebus_topic_t *topic, const void *buf, size_t buf_len) 87 | { 88 | if (topic->buffer_len < buf_len) { 89 | return false; 90 | } 91 | 92 | messagebus_lock_acquire(topic->lock); 93 | 94 | memcpy(topic->buffer, buf, buf_len); 95 | topic->published = true; 96 | messagebus_condvar_broadcast(topic->condvar); 97 | 98 | messagebus_watcher_t *w; 99 | for (w = topic->watchers; w != NULL; w = w->next) { 100 | messagebus_lock_acquire(w->group->lock); 101 | w->group->published_topic = topic; 102 | messagebus_condvar_broadcast(w->group->condvar); 103 | messagebus_lock_release(w->group->lock); 104 | } 105 | 106 | messagebus_lock_release(topic->lock); 107 | 108 | return true; 109 | } 110 | 111 | bool messagebus_topic_read(messagebus_topic_t *topic, void *buf, size_t buf_len) 112 | { 113 | bool success = false; 114 | messagebus_lock_acquire(topic->lock); 115 | 116 | if (topic->published) { 117 | success = true; 118 | memcpy(buf, topic->buffer, buf_len); 119 | } 120 | 121 | messagebus_lock_release(topic->lock); 122 | 123 | return success; 124 | } 125 | 126 | void messagebus_topic_wait(messagebus_topic_t *topic, void *buf, size_t buf_len) 127 | { 128 | messagebus_lock_acquire(topic->lock); 129 | messagebus_condvar_wait(topic->condvar); 130 | 131 | memcpy(buf, topic->buffer, buf_len); 132 | 133 | messagebus_lock_release(topic->lock); 134 | } 135 | 136 | void messagebus_watchgroup_init(messagebus_watchgroup_t *group, void *lock, 137 | void *condvar) 138 | { 139 | group->lock = lock; 140 | group->condvar = condvar; 141 | } 142 | 143 | void messagebus_watchgroup_watch(messagebus_watcher_t *watcher, 144 | messagebus_watchgroup_t *group, 145 | messagebus_topic_t *topic) 146 | { 147 | messagebus_lock_acquire(topic->lock); 148 | messagebus_lock_acquire(group->lock); 149 | 150 | watcher->group = group; 151 | 152 | watcher->next = topic->watchers; 153 | topic->watchers = watcher; 154 | 155 | messagebus_lock_release(group->lock); 156 | messagebus_lock_release(topic->lock); 157 | } 158 | 159 | messagebus_topic_t *messagebus_watchgroup_wait(messagebus_watchgroup_t *group) 160 | { 161 | messagebus_topic_t *res; 162 | 163 | messagebus_lock_acquire(group->lock); 164 | messagebus_condvar_wait(group->condvar); 165 | 166 | res = group->published_topic; 167 | 168 | messagebus_lock_release(group->lock); 169 | 170 | return res; 171 | } 172 | 173 | void messagebus_new_topic_callback_register(messagebus_t *bus, 174 | messagebus_new_topic_cb_t *cb, 175 | void (*cb_fun)(messagebus_t *, 176 | messagebus_topic_t *, 177 | void *), 178 | void *arg) 179 | { 180 | messagebus_lock_acquire(bus->lock); 181 | cb->callback = cb_fun; 182 | cb->callback_arg = arg; 183 | 184 | cb->next = bus->new_topic_callback_list; 185 | bus->new_topic_callback_list = cb; 186 | 187 | messagebus_lock_release(bus->lock); 188 | } 189 | -------------------------------------------------------------------------------- /messagebus.h: -------------------------------------------------------------------------------- 1 | #ifndef MESSAGEBUS_H 2 | #define MESSAGEBUS_H 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | #include 8 | #include 9 | 10 | #define TOPIC_NAME_MAX_LENGTH 64 11 | 12 | typedef struct topic_s { 13 | void *buffer; 14 | size_t buffer_len; 15 | void *lock; 16 | void *condvar; 17 | char name[TOPIC_NAME_MAX_LENGTH + 1]; 18 | bool published; 19 | struct messagebus_watcher_s *watchers; 20 | struct topic_s *next; 21 | void *metadata; 22 | } messagebus_topic_t; 23 | 24 | typedef struct { 25 | struct { 26 | messagebus_topic_t *head; 27 | } topics; 28 | struct messagebus_new_topic_cb_s *new_topic_callback_list; 29 | void *lock; 30 | void *condvar; 31 | } messagebus_t; 32 | 33 | typedef struct messagebus_watchgroup_s { 34 | void *lock; 35 | void *condvar; 36 | messagebus_topic_t *published_topic; 37 | } messagebus_watchgroup_t; 38 | 39 | typedef struct messagebus_watcher_s { 40 | messagebus_watchgroup_t *group; 41 | struct messagebus_watcher_s *next; 42 | } messagebus_watcher_t; 43 | 44 | typedef struct messagebus_new_topic_cb_s { 45 | void (*callback)(messagebus_t *, messagebus_topic_t *, void *); 46 | void *callback_arg; 47 | struct messagebus_new_topic_cb_s *next; 48 | } messagebus_new_topic_cb_t; 49 | 50 | #define MESSAGEBUS_TOPIC_FOREACH(_bus, _topic_var_name) \ 51 | for (int __control = -1; __control < 2; __control++) \ 52 | if (__control < 0) { \ 53 | messagebus_lock_acquire((_bus)->lock); \ 54 | } else if (__control > 0) { \ 55 | messagebus_lock_release((_bus)->lock); \ 56 | } else \ 57 | for (messagebus_topic_t *(_topic_var_name) = (_bus)->topics.head; \ 58 | topic != NULL; \ 59 | (_topic_var_name) = (_topic_var_name)->next) 60 | 61 | 62 | 63 | /** Initializes a topic object 64 | * 65 | * @parameter [in] topic The topic object to create. 66 | * @parameter [in] buffer,buffer_len The buffer where the topic messages will 67 | * @parameter [in] topic_lock The lock to use for this topic. 68 | * @parameter [in] topic_condvar The condition variable to use for this topic. 69 | * be stored. 70 | */ 71 | void messagebus_topic_init(messagebus_topic_t *topic, void *topic_lock, void *topic_condvar, 72 | void *buffer, size_t buffer_len); 73 | 74 | /** Initializes a new message bus with no topics. 75 | * 76 | * @parameter [in] bus The messagebus to init. 77 | * @parameter [in] lock The lock to use for this bus. 78 | * @parameter [in] condvar The condition variable used to signal threads 79 | * waiting on this bus. 80 | */ 81 | void messagebus_init(messagebus_t *bus, void *lock, void *condvar); 82 | 83 | /** Initializes the presence of the topic on the bus. 84 | * 85 | * @parameter [in] bus The bus on which the topic must be advertised. 86 | * @parameter [in] topic The topic object to advertise. 87 | * @parameter [in] name The topic name, used to refer to it from the rest 88 | * of the application. 89 | * 90 | * @note The topic name will be truncated to TOPIC_NAME_MAX_LENGTH characters. 91 | */ 92 | void messagebus_advertise_topic(messagebus_t *bus, messagebus_topic_t *topic, const char *name); 93 | 94 | /** Finds a topic on the bus. 95 | * 96 | * @parameter [in] bus The bus to scan. 97 | * @parameter [in] name The name of the topic to search. 98 | * 99 | * @return A pointer to the topic if it is found, NULL otherwise. 100 | */ 101 | messagebus_topic_t *messagebus_find_topic(messagebus_t *bus, const char *name); 102 | 103 | /** Waits until a topic is found on the bus. 104 | * 105 | * @parameter [in] bus The bus to scan. 106 | * @parameter [in] name The name of the topic to search. 107 | */ 108 | messagebus_topic_t *messagebus_find_topic_blocking(messagebus_t *bus, const char *name); 109 | 110 | /** Publish a topics on the bus. 111 | * 112 | * @parameter [in] topic A pointer to the topic to publish. 113 | * @parameter [in] buf Pointer to a buffer containing the data to publish. 114 | * @parameter [in] buf_len Length of the data buffer. 115 | * 116 | * @warning If the buffer is too big to fit in the topic, no message is sent and 117 | * false is returned. 118 | * @returns True if successful, otherwise. 119 | */ 120 | bool messagebus_topic_publish(messagebus_topic_t *topic, const void *buf, size_t buf_len); 121 | 122 | /** Reads the content of a single topic. 123 | * 124 | * @parameter [in] topic A pointer to the topic to read. 125 | * @parameter [out] buf Pointer where the read data will be stored. 126 | * @parameter [out] buf_len Length of the buffer. 127 | * 128 | * @returns true if the topic was published on at least once. 129 | * @returns false if the topic was never published to 130 | */ 131 | bool messagebus_topic_read(messagebus_topic_t *topic, void *buf, size_t buf_len); 132 | 133 | /** Wait for an update to be published on the topic. 134 | * 135 | * @parameter [in] topic A pointer to the topic to read. 136 | * @parameter [out] buf Pointer where the read data will be stored. 137 | * @parameter [out] buf_len Length of the buffer. 138 | */ 139 | void messagebus_topic_wait(messagebus_topic_t *topic, void *buf, size_t buf_len); 140 | 141 | /** Initializes a watch group. 142 | * 143 | * Watch group are used to wait on a set of topics in parallel (similar to 144 | * select(2) on UNIX). Each watchgroup has a lock and condition variable 145 | * associated to it. 146 | * 147 | * @parameter [in] lock The lock to use for this group. 148 | * @parameter [in] condvar The condition variable to use for this group. 149 | */ 150 | void messagebus_watchgroup_init(messagebus_watchgroup_t *group, void *lock, 151 | void *condvar); 152 | 153 | /** Adds a topic to a given group. 154 | * 155 | * @warning Removing a watchgroup is not supported for now. 156 | */ 157 | void messagebus_watchgroup_watch(messagebus_watcher_t *watcher, 158 | messagebus_watchgroup_t *group, 159 | messagebus_topic_t *topic); 160 | 161 | messagebus_topic_t *messagebus_watchgroup_wait(messagebus_watchgroup_t *group); 162 | 163 | /** Registers a callback that will trigger when a new topic is advertised on 164 | * the bus. */ 165 | void messagebus_new_topic_callback_register(messagebus_t *bus, 166 | messagebus_new_topic_cb_t *cb, 167 | void (*cb_fun)(messagebus_t *, 168 | messagebus_topic_t *, 169 | void *), 170 | void *arg); 171 | 172 | /** @defgroup portable Portable functions, platform specific. 173 | * @{*/ 174 | 175 | /** Acquire a reentrant lock (mutex). */ 176 | extern void messagebus_lock_acquire(void *lock); 177 | 178 | /** Release a lock previously acquired by messagebus_lock_acquire. */ 179 | extern void messagebus_lock_release(void *lock); 180 | 181 | /** Signal all tasks waiting on the given condition variable. */ 182 | extern void messagebus_condvar_broadcast(void *var); 183 | 184 | /** Wait on the given condition variable. */ 185 | extern void messagebus_condvar_wait(void *var); 186 | 187 | /** @} */ 188 | 189 | #ifdef __cplusplus 190 | } 191 | #include "messagebus_cpp.hpp" 192 | #endif 193 | 194 | #endif 195 | -------------------------------------------------------------------------------- /messagebus_cpp.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MESSAGEBUS_CPP_HPP 2 | #define MESSAGEBUS_CPP_HPP 3 | 4 | namespace messagebus 5 | { 6 | template 7 | class TopicWrapper 8 | { 9 | public: 10 | TopicWrapper(messagebus_topic_t *t); 11 | 12 | /// Wrapper around messagebus_topic_publish 13 | void publish(const T &msg); 14 | 15 | /// Wrapper around messagebus_topic_read 16 | bool read(T &msg); 17 | 18 | /// Wrapper around messagebus_topic_wait 19 | T wait(); 20 | 21 | /// Returns true if this wraps a valid topic (i.e. not nullptr) 22 | operator bool(); 23 | 24 | private: 25 | messagebus_topic_t *topic; 26 | }; 27 | 28 | template 29 | TopicWrapper find_topic(messagebus_t &bus, const char *topic_name) 30 | { 31 | auto topic = messagebus_find_topic(&bus, topic_name); 32 | return TopicWrapper(topic); 33 | } 34 | 35 | template 36 | TopicWrapper find_topic_blocking(messagebus_t &bus, const char *topic_name) 37 | { 38 | auto topic = messagebus_find_topic_blocking(&bus, topic_name); 39 | return TopicWrapper(topic); 40 | } 41 | 42 | template 43 | TopicWrapper::TopicWrapper(messagebus_topic_t *t) 44 | : topic(t) 45 | { 46 | } 47 | 48 | template 49 | void TopicWrapper::publish(const T &msg) 50 | { 51 | messagebus_topic_publish(topic, &msg, sizeof(T)); 52 | } 53 | 54 | template 55 | bool TopicWrapper::read(T &msg) 56 | { 57 | return messagebus_topic_read(topic, &msg, sizeof(T)); 58 | } 59 | 60 | template 61 | T TopicWrapper::wait() 62 | { 63 | T res; 64 | messagebus_topic_wait(topic, &res, sizeof(T)); 65 | return res; 66 | } 67 | 68 | template 69 | TopicWrapper::operator bool() 70 | { 71 | return topic != nullptr; 72 | } 73 | 74 | } // namespace messagebus 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /package.yml: -------------------------------------------------------------------------------- 1 | depends: 2 | - test-runner 3 | 4 | source: 5 | - messagebus.c 6 | 7 | tests: 8 | - tests/mocks/synchronization.cpp 9 | - tests/atomicity.cpp 10 | - tests/msgbus.cpp 11 | - tests/signaling.cpp 12 | - tests/foreach.cpp 13 | - tests/watchgroups.cpp 14 | - tests/new_topic_callbacks.cpp 15 | - tests/test_cpp_interface.cpp 16 | 17 | target.demo: 18 | - examples/posix/demo.c 19 | - examples/posix/port.c 20 | 21 | target.demo_watchgroups: 22 | - examples/posix/demo_watchgroups.c 23 | - examples/posix/port.c 24 | 25 | target.arm: 26 | - examples/chibios/port.c 27 | 28 | templates: 29 | demo.jinja: CMakeLists.txt 30 | -------------------------------------------------------------------------------- /tests/atomicity.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../messagebus.h" 4 | #include "mocks/synchronization.hpp" 5 | 6 | TEST_GROUP(MessageBusAtomicityTestGroup) 7 | { 8 | messagebus_t bus; 9 | int bus_lock, bus_condvar; 10 | messagebus_topic_t topic; 11 | uint8_t buffer[128]; 12 | int topic_lock; 13 | int topic_condvar; 14 | 15 | void setup() 16 | { 17 | mock().strictOrder(); 18 | 19 | messagebus_init(&bus, &bus_lock, &bus_condvar); 20 | messagebus_topic_init(&topic, &topic_lock, &topic_condvar, buffer, sizeof buffer); 21 | } 22 | 23 | void teardown() 24 | { 25 | lock_mocks_enable(false); 26 | mock().checkExpectations(); 27 | mock().clear(); 28 | } 29 | }; 30 | 31 | TEST(MessageBusAtomicityTestGroup, AdvertiseIsLockedProperly) 32 | { 33 | mock().expectOneCall("messagebus_lock_acquire") 34 | .withPointerParameter("lock", bus.lock); 35 | mock().expectOneCall("messagebus_lock_release") 36 | .withPointerParameter("lock", bus.lock); 37 | 38 | lock_mocks_enable(true); 39 | messagebus_advertise_topic(&bus, &topic, "topic"); 40 | } 41 | 42 | TEST(MessageBusAtomicityTestGroup, FindNoneIsLockedProperly) 43 | { 44 | mock().expectOneCall("messagebus_lock_acquire") 45 | .withPointerParameter("lock", bus.lock); 46 | mock().expectOneCall("messagebus_lock_release") 47 | .withPointerParameter("lock", bus.lock); 48 | 49 | lock_mocks_enable(true); 50 | messagebus_find_topic(&bus, "topic"); 51 | } 52 | 53 | TEST(MessageBusAtomicityTestGroup, FindExistingTopicIsLockedProperly) 54 | { 55 | mock().expectOneCall("messagebus_lock_acquire") 56 | .withPointerParameter("lock", bus.lock); 57 | mock().expectOneCall("messagebus_lock_release") 58 | .withPointerParameter("lock", bus.lock); 59 | 60 | messagebus_advertise_topic(&bus, &topic, "topic"); 61 | lock_mocks_enable(true); 62 | messagebus_find_topic(&bus, "topic"); 63 | } 64 | 65 | TEST(MessageBusAtomicityTestGroup, PublishIsAtomic) 66 | { 67 | uint8_t data[4]; 68 | mock().expectOneCall("messagebus_lock_acquire") 69 | .withPointerParameter("lock", topic.lock); 70 | mock().expectOneCall("messagebus_lock_release") 71 | .withPointerParameter("lock", topic.lock); 72 | 73 | lock_mocks_enable(true); 74 | messagebus_topic_publish(&topic, data, 4); 75 | } 76 | 77 | TEST(MessageBusAtomicityTestGroup, ReadPublished) 78 | { 79 | uint8_t buffer[128]; 80 | bool res; 81 | 82 | mock().expectOneCall("messagebus_lock_acquire") 83 | .withPointerParameter("lock", topic.lock); 84 | mock().expectOneCall("messagebus_lock_release") 85 | .withPointerParameter("lock", topic.lock); 86 | 87 | messagebus_topic_publish(&topic, buffer, sizeof(buffer)); 88 | 89 | lock_mocks_enable(true); 90 | res = messagebus_topic_read(&topic, buffer, sizeof(buffer)); 91 | 92 | CHECK_TRUE(res); 93 | } 94 | 95 | TEST(MessageBusAtomicityTestGroup, ReadUnpublished) 96 | { 97 | uint8_t buffer[128]; 98 | bool res; 99 | mock().expectOneCall("messagebus_lock_acquire") 100 | .withPointerParameter("lock", topic.lock); 101 | mock().expectOneCall("messagebus_lock_release") 102 | .withPointerParameter("lock", topic.lock); 103 | 104 | lock_mocks_enable(true); 105 | res = messagebus_topic_read(&topic, buffer, sizeof(buffer)); 106 | 107 | CHECK_FALSE(res); 108 | } 109 | 110 | TEST(MessageBusAtomicityTestGroup, Wait) 111 | { 112 | uint8_t buffer[128]; 113 | mock().expectOneCall("messagebus_lock_acquire") 114 | .withPointerParameter("lock", topic.lock); 115 | mock().expectOneCall("messagebus_lock_release") 116 | .withPointerParameter("lock", topic.lock); 117 | 118 | lock_mocks_enable(true); 119 | messagebus_topic_wait(&topic, buffer, sizeof(buffer)); 120 | } 121 | 122 | TEST(MessageBusAtomicityTestGroup, FindBlocking) 123 | { 124 | messagebus_advertise_topic(&bus, &topic, "topic"); 125 | lock_mocks_enable(true); 126 | 127 | mock().expectOneCall("messagebus_lock_acquire") 128 | .withPointerParameter("lock", bus.lock); 129 | 130 | mock().expectOneCall("messagebus_lock_release") 131 | .withPointerParameter("lock", bus.lock); 132 | 133 | messagebus_find_topic_blocking(&bus, "topic"); 134 | } 135 | 136 | TEST(MessageBusAtomicityTestGroup, RegisterNewTopicCallback) 137 | { 138 | messagebus_advertise_topic(&bus, &topic, "topic"); 139 | messagebus_new_topic_cb_t cb; 140 | 141 | lock_mocks_enable(true); 142 | 143 | mock().expectOneCall("messagebus_lock_acquire") 144 | .withPointerParameter("lock", bus.lock); 145 | 146 | mock().expectOneCall("messagebus_lock_release") 147 | .withPointerParameter("lock", bus.lock); 148 | 149 | messagebus_new_topic_callback_register(&bus, &cb, nullptr, nullptr); 150 | } 151 | -------------------------------------------------------------------------------- /tests/foreach.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../messagebus.h" 5 | #include "mocks/synchronization.hpp" 6 | 7 | TEST_GROUP(MsgBusForeachTestGroup) 8 | { 9 | messagebus_t bus; 10 | messagebus_topic_t foo, bar; 11 | 12 | void setup() 13 | { 14 | int bus_lock; 15 | messagebus_init(&bus, &bus_lock, NULL); 16 | messagebus_topic_init(&foo, NULL, NULL, NULL, 0); 17 | messagebus_topic_init(&bar, NULL, NULL, NULL, 0); 18 | messagebus_advertise_topic(&bus, &foo, "foo"); 19 | messagebus_advertise_topic(&bus, &bar, "bar"); 20 | } 21 | 22 | void teardown() 23 | { 24 | lock_mocks_enable(false); 25 | mock().checkExpectations(); 26 | mock().clear(); 27 | } 28 | }; 29 | 30 | TEST(MsgBusForeachTestGroup, CanIterate) 31 | { 32 | mock().expectOneCall("loop").withParameter("topic", &foo); 33 | mock().expectOneCall("loop").withParameter("topic", &bar); 34 | 35 | MESSAGEBUS_TOPIC_FOREACH(&bus, topic) { 36 | mock().actualCall("loop").withParameter("topic", topic); 37 | } 38 | } 39 | 40 | TEST(MsgBusForeachTestGroup, CanLock) 41 | { 42 | lock_mocks_enable(true); 43 | mock().expectOneCall("messagebus_lock_acquire").withPointerParameter("lock", bus.lock); 44 | mock().expectOneCall("messagebus_lock_release").withPointerParameter("lock", bus.lock); 45 | 46 | MESSAGEBUS_TOPIC_FOREACH(&bus, topic) { 47 | } 48 | } 49 | 50 | TEST(MsgBusForeachTestGroup, CanBreak) 51 | { 52 | lock_mocks_enable(true); 53 | mock().expectOneCall("messagebus_lock_acquire").withPointerParameter("lock", bus.lock); 54 | mock().expectOneCall("messagebus_lock_release").withPointerParameter("lock", bus.lock); 55 | 56 | MESSAGEBUS_TOPIC_FOREACH(&bus, topic) { 57 | break; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/mocks/synchronization.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../../messagebus.h" 4 | 5 | static bool lock_enabled = false; 6 | static bool condvar_enabled = false; 7 | 8 | void messagebus_lock_acquire(void *lock) 9 | { 10 | if (lock_enabled) { 11 | mock().actualCall("messagebus_lock_acquire") 12 | .withPointerParameter("lock", lock); 13 | } 14 | } 15 | 16 | void messagebus_lock_release(void *lock) 17 | { 18 | if (lock_enabled) { 19 | mock().actualCall("messagebus_lock_release") 20 | .withPointerParameter("lock", lock); 21 | } 22 | } 23 | 24 | void messagebus_condvar_broadcast(void *var) 25 | { 26 | if (condvar_enabled) { 27 | mock().actualCall("messagebus_condvar_broadcast") 28 | .withPointerParameter("var", var); 29 | } 30 | } 31 | 32 | void messagebus_condvar_wait(void *var) 33 | { 34 | if (condvar_enabled) { 35 | mock().actualCall("messagebus_condvar_wait") 36 | .withPointerParameter("var", var); 37 | } 38 | } 39 | 40 | void lock_mocks_enable(bool enabled) 41 | { 42 | lock_enabled = enabled; 43 | } 44 | 45 | void condvar_mocks_enable(bool enabled) 46 | { 47 | condvar_enabled = enabled; 48 | } 49 | 50 | TEST_GROUP(LockTestGroup) 51 | { 52 | int lock; 53 | 54 | void setup() 55 | { 56 | lock_mocks_enable(true); 57 | condvar_mocks_enable(true); 58 | } 59 | 60 | void teardown() 61 | { 62 | lock_mocks_enable(false); 63 | condvar_mocks_enable(false); 64 | mock().checkExpectations(); 65 | mock().clear(); 66 | } 67 | }; 68 | 69 | TEST(LockTestGroup, CanLock) 70 | { 71 | mock().expectOneCall("messagebus_lock_acquire") 72 | .withPointerParameter("lock", &lock); 73 | 74 | messagebus_lock_acquire(&lock); 75 | } 76 | 77 | TEST(LockTestGroup, CanUnlock) 78 | { 79 | mock().expectOneCall("messagebus_lock_release") 80 | .withPointerParameter("lock", &lock); 81 | 82 | messagebus_lock_release(&lock); 83 | } 84 | 85 | TEST(LockTestGroup, CanBroadcastCondVar) 86 | { 87 | int var; 88 | mock().expectOneCall("messagebus_condvar_broadcast") 89 | .withPointerParameter("var", &var); 90 | 91 | messagebus_condvar_broadcast(&var); 92 | } 93 | 94 | TEST(LockTestGroup, CanWaitCondVar) 95 | { 96 | int var; 97 | 98 | mock().expectOneCall("messagebus_condvar_wait") 99 | .withPointerParameter("var", &var); 100 | 101 | 102 | messagebus_condvar_wait(&var); 103 | } 104 | -------------------------------------------------------------------------------- /tests/mocks/synchronization.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SYNCHRONIZATION_HPP 2 | #define SYNCHRONIZATION_HPP 3 | 4 | void lock_mocks_enable(bool enabled); 5 | void condvar_mocks_enable(bool enabled); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /tests/msgbus.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../messagebus.h" 4 | 5 | TEST_GROUP(MessageBusTestGroup) 6 | { 7 | messagebus_t bus; 8 | int bus_lock; 9 | int bus_condvar; 10 | messagebus_topic_t topic; 11 | uint8_t buffer[128]; 12 | int topic_lock; 13 | int topic_condvar; 14 | messagebus_topic_t second_topic; 15 | 16 | void setup() 17 | { 18 | messagebus_init(&bus, &bus_lock, &bus_condvar); 19 | messagebus_topic_init(&topic, &topic_lock, &topic_condvar, buffer, sizeof buffer); 20 | messagebus_topic_init(&second_topic, NULL, NULL, NULL, 0); 21 | } 22 | }; 23 | 24 | TEST(MessageBusTestGroup, CanCreateTopicWithBuffer) 25 | { 26 | POINTERS_EQUAL(buffer, topic.buffer); 27 | POINTERS_EQUAL(&topic_lock, topic.lock); 28 | POINTERS_EQUAL(&topic_condvar, topic.condvar); 29 | CHECK_EQUAL(topic.buffer_len, sizeof(buffer)); 30 | } 31 | 32 | TEST(MessageBusTestGroup, HasMetadataField) 33 | { 34 | POINTERS_EQUAL(NULL, topic.metadata); 35 | } 36 | 37 | TEST(MessageBusTestGroup, CanCreateBus) 38 | { 39 | POINTERS_EQUAL(NULL, bus.topics.head); 40 | POINTERS_EQUAL(&bus_lock, bus.lock); 41 | POINTERS_EQUAL(&bus_condvar, bus.condvar); 42 | } 43 | 44 | TEST(MessageBusTestGroup, AdvertiseTopicName) 45 | { 46 | messagebus_advertise_topic(&bus, &topic, "/imu/raw"); 47 | 48 | STRCMP_EQUAL("/imu/raw", topic.name); 49 | } 50 | 51 | TEST(MessageBusTestGroup, FirstTopicGoesToHead) 52 | { 53 | messagebus_advertise_topic(&bus, &topic, "/imu/raw"); 54 | 55 | POINTERS_EQUAL(&topic, bus.topics.head); 56 | } 57 | 58 | TEST(MessageBusTestGroup, NextofListIsOkToo) 59 | { 60 | messagebus_advertise_topic(&bus, &topic, "first"); 61 | messagebus_advertise_topic(&bus, &second_topic, "second"); 62 | 63 | POINTERS_EQUAL(&second_topic, bus.topics.head); 64 | POINTERS_EQUAL(&topic, bus.topics.head->next); 65 | } 66 | 67 | TEST(MessageBusTestGroup, TopicNotFound) 68 | { 69 | messagebus_find_topic(&bus, "topic"); 70 | POINTERS_EQUAL(NULL, messagebus_find_topic(&bus, "topic")); 71 | } 72 | 73 | TEST(MessageBusTestGroup, TopicFound) 74 | { 75 | messagebus_advertise_topic(&bus, &topic, "topic"); 76 | POINTERS_EQUAL(&topic, messagebus_find_topic(&bus, "topic")); 77 | } 78 | 79 | TEST(MessageBusTestGroup, CanScanBus) 80 | { 81 | messagebus_advertise_topic(&bus, &topic, "first"); 82 | messagebus_advertise_topic(&bus, &second_topic, "second"); 83 | 84 | POINTERS_EQUAL(&topic, messagebus_find_topic(&bus, "first")); 85 | POINTERS_EQUAL(&second_topic, messagebus_find_topic(&bus, "second")); 86 | } 87 | 88 | TEST(MessageBusTestGroup, FindTopicBlocking) 89 | { 90 | messagebus_topic_t *res; 91 | /* This is a partial test only: we cannot test that the behavior is correct 92 | * when the topic is not on the bus yes without additional thread and I 93 | * don't like threading in tests. */ 94 | messagebus_advertise_topic(&bus, &topic, "topic"); 95 | res = messagebus_find_topic_blocking(&bus, "topic"); 96 | POINTERS_EQUAL(&topic, res); 97 | } 98 | 99 | TEST(MessageBusTestGroup, CanPublish) 100 | { 101 | uint8_t data[] = {1, 2, 3}; 102 | bool res; 103 | 104 | res = messagebus_topic_publish(&topic, data, sizeof(data)); 105 | 106 | MEMCMP_EQUAL(topic.buffer, data, sizeof(data)); 107 | CHECK_TRUE(res); 108 | } 109 | 110 | TEST(MessageBusTestGroup, WontPublishTooBigMessage) 111 | { 112 | uint8_t data[] = {1, 2, 3}; 113 | bool res; 114 | 115 | topic.buffer_len = 1; 116 | res = messagebus_topic_publish(&topic, data, sizeof(data)); 117 | 118 | CHECK_FALSE(res); 119 | } 120 | 121 | TEST(MessageBusTestGroup, CanRead) 122 | { 123 | int tx=42, rx; 124 | bool res; 125 | 126 | messagebus_topic_publish(&topic, &tx, sizeof(int)); 127 | res = messagebus_topic_read(&topic, &rx, sizeof(int)); 128 | 129 | CHECK_TRUE(res); 130 | CHECK_EQUAL(tx, rx); 131 | } 132 | 133 | TEST(MessageBusTestGroup, WontReadUnpublishedtopic) 134 | { 135 | int rx; 136 | bool res; 137 | 138 | res = messagebus_topic_read(&topic, &rx, sizeof(int)); 139 | CHECK_FALSE(res); 140 | } 141 | 142 | TEST(MessageBusTestGroup, WaitForUpdate) 143 | { 144 | int tx=42, rx; 145 | 146 | messagebus_topic_publish(&topic, &tx, sizeof(int)); 147 | messagebus_topic_wait(&topic, &rx, sizeof(int)); 148 | 149 | CHECK_EQUAL(tx, rx); 150 | } 151 | -------------------------------------------------------------------------------- /tests/new_topic_callbacks.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../messagebus.h" 4 | 5 | TEST_GROUP(NewTopicCallback) 6 | { 7 | messagebus_t bus; 8 | messagebus_topic_t topic; 9 | messagebus_new_topic_cb_t cb; 10 | 11 | void setup() 12 | { 13 | messagebus_init(&bus, NULL, NULL); 14 | messagebus_topic_init(&topic, NULL, NULL, NULL, 0); 15 | } 16 | }; 17 | 18 | void my_cb(messagebus_t *bus, messagebus_topic_t *topic, void *arg) 19 | { 20 | mock().actualCall("my_cb") 21 | .withPointerParameter("bus", bus) 22 | .withPointerParameter("topic", topic) 23 | .withPointerParameter("arg", arg); 24 | } 25 | 26 | TEST(NewTopicCallback, CanRegisterCallback) 27 | { 28 | messagebus_new_topic_callback_register(&bus, &cb, my_cb, (void *)0x1234); 29 | 30 | POINTERS_EQUAL(my_cb, cb.callback); 31 | POINTERS_EQUAL((void *)0x1234, cb.callback_arg); 32 | POINTERS_EQUAL(&cb, bus.new_topic_callback_list); 33 | POINTERS_EQUAL(NULL, cb.next); 34 | } 35 | 36 | 37 | TEST(NewTopicCallback, CallbackFiresSeveralTime) 38 | { 39 | messagebus_new_topic_cb_t cb2; 40 | messagebus_new_topic_callback_register(&bus, &cb, my_cb, (void *)0x1234); 41 | messagebus_new_topic_callback_register(&bus, &cb2, my_cb, (void *)0x4321); 42 | 43 | POINTERS_EQUAL(&cb, cb2.next); 44 | 45 | mock().expectOneCall("my_cb") 46 | .withPointerParameter("bus", &bus) 47 | .withPointerParameter("topic", &topic) 48 | .withPointerParameter("arg", (void *)0x1234); 49 | mock().expectOneCall("my_cb") 50 | .withPointerParameter("bus", &bus) 51 | .withPointerParameter("topic", &topic) 52 | .withPointerParameter("arg", (void *)0x4321); 53 | messagebus_advertise_topic(&bus, &topic, "/foo"); 54 | } 55 | -------------------------------------------------------------------------------- /tests/signaling.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../messagebus.h" 4 | #include "mocks/synchronization.hpp" 5 | 6 | TEST_GROUP(SignalingTestGroup) 7 | { 8 | messagebus_t bus; 9 | int bus_lock, bus_condvar; 10 | messagebus_topic_t topic; 11 | uint8_t buffer[128]; 12 | int topic_lock; 13 | int topic_condvar; 14 | 15 | void setup() 16 | { 17 | mock().strictOrder(); 18 | messagebus_init(&bus, &bus_lock, &bus_condvar); 19 | messagebus_topic_init(&topic, &topic_lock, &topic_condvar, buffer, sizeof buffer); 20 | } 21 | 22 | void teardown() 23 | { 24 | lock_mocks_enable(false); 25 | condvar_mocks_enable(false); 26 | mock().checkExpectations(); 27 | mock().clear(); 28 | } 29 | }; 30 | 31 | TEST(SignalingTestGroup, TopicPublish) 32 | { 33 | lock_mocks_enable(true); 34 | condvar_mocks_enable(true); 35 | 36 | mock().expectOneCall("messagebus_lock_acquire") 37 | .withPointerParameter("lock", topic.lock); 38 | 39 | mock().expectOneCall("messagebus_condvar_broadcast") 40 | .withPointerParameter("var", topic.condvar); 41 | 42 | mock().expectOneCall("messagebus_lock_release") 43 | .withPointerParameter("lock", topic.lock); 44 | 45 | messagebus_topic_publish(&topic, buffer, sizeof(buffer)); 46 | } 47 | 48 | TEST(SignalingTestGroup, TopicWait) 49 | { 50 | lock_mocks_enable(true); 51 | condvar_mocks_enable(true); 52 | 53 | mock().expectOneCall("messagebus_lock_acquire") 54 | .withPointerParameter("lock", topic.lock); 55 | 56 | mock().expectOneCall("messagebus_condvar_wait") 57 | .withPointerParameter("var", topic.condvar); 58 | 59 | mock().expectOneCall("messagebus_lock_release") 60 | .withPointerParameter("lock", topic.lock); 61 | 62 | messagebus_topic_wait(&topic, buffer, sizeof(buffer)); 63 | } 64 | 65 | TEST(SignalingTestGroup, Advertise) 66 | { 67 | lock_mocks_enable(true); 68 | condvar_mocks_enable(true); 69 | 70 | mock().expectOneCall("messagebus_lock_acquire") 71 | .withPointerParameter("lock", bus.lock); 72 | 73 | mock().expectOneCall("messagebus_condvar_broadcast") 74 | .withPointerParameter("var", bus.condvar); 75 | 76 | mock().expectOneCall("messagebus_lock_release") 77 | .withPointerParameter("lock", bus.lock); 78 | 79 | messagebus_advertise_topic(&bus, &topic, "topic"); 80 | } 81 | -------------------------------------------------------------------------------- /tests/test_cpp_interface.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../messagebus.h" 4 | 5 | 6 | TEST_GROUP(MessagebusCppInterface) 7 | { 8 | messagebus_topic_t raw_topic; 9 | int topic_content; 10 | messagebus_t bus; 11 | messagebus::TopicWrapper topic{&raw_topic}; 12 | void setup(void) 13 | { 14 | messagebus_topic_init(&raw_topic, nullptr, nullptr, &topic_content, sizeof(topic_content)); 15 | messagebus_init(&bus, nullptr, nullptr); 16 | messagebus_advertise_topic(&bus, &raw_topic, "/foo"); 17 | } 18 | }; 19 | 20 | TEST(MessagebusCppInterface, CanPublish) 21 | { 22 | topic.publish(42); 23 | 24 | int read_msg; 25 | bool res = messagebus_topic_read(&raw_topic, &read_msg, sizeof(read_msg)); 26 | 27 | CHECK_TRUE(res); 28 | CHECK_EQUAL(42, read_msg); 29 | } 30 | 31 | TEST(MessagebusCppInterface, CanRead) 32 | { 33 | int msg = 42; 34 | messagebus_topic_publish(&raw_topic, &msg, sizeof(msg)); 35 | 36 | int read_msg; 37 | bool res = topic.read(read_msg); 38 | 39 | CHECK_TRUE(res); 40 | CHECK_EQUAL(msg, read_msg); 41 | } 42 | 43 | TEST(MessagebusCppInterface, CanWait) 44 | { 45 | int msg = 42; 46 | messagebus_topic_publish(&raw_topic, &msg, sizeof(msg)); 47 | 48 | CHECK_EQUAL(42, topic.wait()); 49 | } 50 | 51 | TEST(MessagebusCppInterface, CanCheckIfTopicExists) 52 | { 53 | CHECK_TRUE(messagebus::TopicWrapper(&raw_topic)); 54 | CHECK_FALSE(messagebus::TopicWrapper(nullptr)); 55 | } 56 | 57 | TEST(MessagebusCppInterface, CanFindTopic) 58 | { 59 | auto topic = messagebus::find_topic(bus, "/foo"); 60 | CHECK_TRUE(topic); 61 | } 62 | 63 | TEST(MessagebusCppInterface, CanFindTopicBlocking) 64 | { 65 | auto topic = messagebus::find_topic_blocking(bus, "/foo"); 66 | CHECK_TRUE(topic); 67 | } 68 | -------------------------------------------------------------------------------- /tests/watchgroups.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../messagebus.h" 5 | #include "mocks/synchronization.hpp" 6 | 7 | TEST_GROUP(Watchgroups) 8 | { 9 | messagebus_watchgroup_t group; 10 | messagebus_topic_t topic; 11 | int lock, condvar; 12 | messagebus_watcher_t watcher; 13 | 14 | void setup() 15 | { 16 | messagebus_topic_init(&topic, NULL, NULL, NULL, 0); 17 | messagebus_watchgroup_init(&group, &lock, &condvar); 18 | } 19 | 20 | void teardown() 21 | { 22 | lock_mocks_enable(false); 23 | condvar_mocks_enable(false); 24 | } 25 | }; 26 | 27 | TEST(Watchgroups, CanInitWatchGroup) 28 | { 29 | POINTERS_EQUAL(&lock, group.lock); 30 | POINTERS_EQUAL(&condvar, group.condvar); 31 | } 32 | 33 | TEST(Watchgroups, CanAddTopicToGroup) 34 | { 35 | messagebus_watchgroup_watch(&watcher, &group, &topic); 36 | POINTERS_EQUAL(&group, topic.watchers->group); 37 | } 38 | 39 | TEST(Watchgroups, CanAddTopicToDifferentGroups) 40 | { 41 | messagebus_watcher_t w2; 42 | messagebus_watchgroup_t second_group; 43 | messagebus_watchgroup_init(&second_group, NULL, NULL); 44 | 45 | messagebus_watchgroup_watch(&watcher, &group, &topic); 46 | messagebus_watchgroup_watch(&w2, &second_group, &topic); 47 | 48 | POINTERS_EQUAL(&second_group, topic.watchers->group); 49 | POINTERS_EQUAL(&group, topic.watchers->next->group); 50 | } 51 | 52 | TEST(Watchgroups, CanWaitOnGroup) 53 | { 54 | group.published_topic = &topic; 55 | 56 | lock_mocks_enable(true); 57 | condvar_mocks_enable(true); 58 | 59 | mock().strictOrder(); 60 | mock().expectOneCall("messagebus_lock_acquire").withPointerParameter("lock", group.lock); 61 | mock().expectOneCall("messagebus_condvar_wait").withPointerParameter("var", group.condvar); 62 | mock().expectOneCall("messagebus_lock_release").withPointerParameter("lock", group.lock); 63 | 64 | auto res = messagebus_watchgroup_wait(&group); 65 | POINTERS_EQUAL(&topic, res); 66 | } 67 | 68 | TEST(Watchgroups, GroupIsWokeUpOnPublish) 69 | { 70 | messagebus_watchgroup_watch(&watcher, &group, &topic); 71 | 72 | mock().strictOrder(); 73 | 74 | /* Normal publish on bus */ 75 | mock().expectOneCall("messagebus_lock_acquire").withPointerParameter("lock", topic.lock); 76 | mock().expectOneCall("messagebus_condvar_broadcast").withPointerParameter("var", topic.condvar); 77 | 78 | mock().expectOneCall("messagebus_lock_acquire").withPointerParameter("lock", group.lock); 79 | mock().expectOneCall("messagebus_condvar_broadcast").withPointerParameter("var", group.condvar); 80 | mock().expectOneCall("messagebus_lock_release").withPointerParameter("lock", group.lock); 81 | 82 | 83 | mock().expectOneCall("messagebus_lock_release").withPointerParameter("lock", topic.lock); 84 | 85 | lock_mocks_enable(true); 86 | condvar_mocks_enable(true); 87 | 88 | messagebus_topic_publish(&topic, NULL, 0); 89 | } 90 | 91 | TEST(Watchgroups, GroupHasTopic) 92 | { 93 | messagebus_watchgroup_watch(&watcher, &group, &topic); 94 | messagebus_topic_publish(&topic, NULL, 0); 95 | POINTERS_EQUAL(&topic, group.published_topic); 96 | } 97 | 98 | TEST(Watchgroups, AllGroupsAreSignaled) 99 | { 100 | messagebus_watchgroup_t group2; 101 | int lock2, var2; 102 | messagebus_watcher_t w2; 103 | 104 | messagebus_watchgroup_watch(&watcher, &group, &topic); 105 | 106 | messagebus_watchgroup_init(&group, &lock2, &var2); 107 | messagebus_watchgroup_watch(&w2, &group2, &topic); 108 | 109 | /* Normal publish on bus */ 110 | mock().expectOneCall("messagebus_lock_acquire").withPointerParameter("lock", topic.lock); 111 | mock().expectOneCall("messagebus_condvar_broadcast").withPointerParameter("var", topic.condvar); 112 | 113 | 114 | /* First group */ 115 | mock().expectOneCall("messagebus_lock_acquire").withPointerParameter("lock", group.lock); 116 | mock().expectOneCall("messagebus_condvar_broadcast").withPointerParameter("var", group.condvar); 117 | mock().expectOneCall("messagebus_lock_release").withPointerParameter("lock", group.lock); 118 | 119 | /* Second group */ 120 | mock().expectOneCall("messagebus_lock_acquire").withPointerParameter("lock", group2.lock); 121 | mock().expectOneCall("messagebus_condvar_broadcast").withPointerParameter("var", group2.condvar); 122 | mock().expectOneCall("messagebus_lock_release").withPointerParameter("lock", group2.lock); 123 | 124 | mock().expectOneCall("messagebus_lock_release").withPointerParameter("lock", topic.lock); 125 | 126 | lock_mocks_enable(true); 127 | condvar_mocks_enable(true); 128 | 129 | messagebus_topic_publish(&topic, NULL, 0); 130 | 131 | } 132 | 133 | /* This test checks that we can have several group containing the same topics. */ 134 | TEST(Watchgroups, GroupCanBeLinkedInMultipleTopics) 135 | { 136 | messagebus_topic_t topic2; 137 | messagebus_topic_init(&topic2, NULL, NULL, NULL, 0); 138 | 139 | messagebus_watchgroup_t group2, group3; 140 | int lock2, var2; 141 | 142 | messagebus_watchgroup_init(&group2, &lock2, &var2); 143 | messagebus_watchgroup_init(&group3, NULL, NULL); 144 | 145 | 146 | /* Adds the first topic to two groups. */ 147 | messagebus_watcher_t watcher2; 148 | messagebus_watchgroup_watch(&watcher, &group, &topic); 149 | messagebus_watchgroup_watch(&watcher2, &group2, &topic); 150 | 151 | /* Adds the second topic to two groups as well. */ 152 | messagebus_watcher_t watcher3, watcher1_2; 153 | messagebus_watchgroup_watch(&watcher3, &group3, &topic2); 154 | messagebus_watchgroup_watch(&watcher1_2, &group, &topic2); 155 | 156 | /* Normal publish on first topic */ 157 | mock().expectOneCall("messagebus_lock_acquire").withPointerParameter("lock", topic.lock); 158 | mock().expectOneCall("messagebus_condvar_broadcast").withPointerParameter("var", topic.condvar); 159 | 160 | 161 | /* Published topic belongs to groups 1 & 2, lets start with group1. */ 162 | mock().expectOneCall("messagebus_lock_acquire").withPointerParameter("lock", group.lock); 163 | mock().expectOneCall("messagebus_condvar_broadcast").withPointerParameter("var", group.condvar); 164 | mock().expectOneCall("messagebus_lock_release").withPointerParameter("lock", group.lock); 165 | 166 | /* Then group 2. */ 167 | mock().expectOneCall("messagebus_lock_acquire").withPointerParameter("lock", group2.lock); 168 | mock().expectOneCall("messagebus_condvar_broadcast").withPointerParameter("var", group2.condvar); 169 | mock().expectOneCall("messagebus_lock_release").withPointerParameter("lock", group2.lock); 170 | 171 | /* Also a part of the normal publish cycle */ 172 | mock().expectOneCall("messagebus_lock_release").withPointerParameter("lock", topic.lock); 173 | 174 | /* Finally, do the actuall publish. */ 175 | lock_mocks_enable(true); 176 | condvar_mocks_enable(true); 177 | messagebus_topic_publish(&topic, NULL, 0); 178 | } 179 | 180 | TEST(Watchgroups, WatcherAnAlreadyWatchedTopicIsInitialized) 181 | { 182 | int lock, condvar; 183 | messagebus_watchgroup_t second_group; 184 | messagebus_watcher_t second_watcher; 185 | 186 | // Poison the watchers to simulate uninitialized memory 187 | memset(&second_group, 0x55, sizeof(second_group)); 188 | memset(&second_watcher, 0x55, sizeof(second_watcher)); 189 | 190 | messagebus_watchgroup_init(&second_group, &lock, &condvar); 191 | 192 | messagebus_watchgroup_watch(&second_watcher, &second_group, &topic); 193 | messagebus_watchgroup_watch(&watcher, &group, &topic); 194 | 195 | POINTERS_EQUAL(NULL, second_watcher.next); 196 | 197 | // It will crash if the watchers are not properly initialized 198 | messagebus_topic_publish(&topic, NULL, 0); 199 | } 200 | --------------------------------------------------------------------------------