├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── autogen-config.json ├── cmake └── ev3dev-config.cmake ├── demos ├── CMakeLists.txt ├── README.md ├── button-test.cpp ├── drive-test.cpp ├── ev3dev-lang-demo.cpp ├── ev3dev-lang-test.cpp ├── remote-control.cpp ├── remote_control-test.cpp └── sound-test.cpp ├── ev3dev.cpp ├── ev3dev.h ├── templates ├── autogen-header.liquid ├── generic-class-description.liquid ├── generic-declare-property-value.liquid ├── generic-define-property-value.liquid ├── generic-get-set.liquid ├── generic_report_status.liquid ├── leds-declare.liquid ├── leds-define.liquid ├── motor_commands.liquid └── special-sensor-declaration.liquid └── tests ├── CMakeLists.txt ├── api_tests.cpp └── catch.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | bin 4 | lib 5 | obj 6 | build 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tests/fake-sys"] 2 | path = tests/fake-sys 3 | url = https://github.com/ev3dev/ev3dev-lang-fake-sys.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | language: cpp 3 | compiler: 4 | - gcc 5 | 6 | addons: 7 | apt: 8 | sources: 9 | - ubuntu-toolchain-r-test 10 | packages: 11 | - gcc-5 12 | - g++-5 13 | 14 | script: 15 | - mkdir -p build && cd build 16 | - CC=gcc-5 CXX=g++-5 cmake .. 17 | - make 18 | - ctest --output-on-failure 19 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | if (NOT CMAKE_BUILD_TYPE) 3 | message(STATUS "No build type selected, default to RelWithDebInfo") 4 | set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Build type") 5 | endif() 6 | 7 | set(CMAKE_CXX_STANDARD 11) 8 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 9 | 10 | project(ev3dev-lang-cpp) 11 | 12 | # Is this used directly or via add_subdirectory() 13 | set(EV3DEV_LANG_CPP_MASTER_PROJECT OFF) 14 | if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 15 | set(EV3DEV_LANG_CPP_MASTER_PROJECT ON) 16 | endif() 17 | 18 | set(EV3DEV_PLATFORM "EV3" CACHE STRING "Target ev3dev platform (EV3/BRICKPI/BRICKPI3/PISTORMS)") 19 | set_property(CACHE EV3DEV_PLATFORM PROPERTY STRINGS "EV3" "BRICKPI" "BRICKPI3" "PISTORMS") 20 | 21 | add_library(ev3dev STATIC ev3dev.cpp) 22 | add_library(ev3dev::ev3dev ALIAS ev3dev) # to match exported target 23 | 24 | target_include_directories(ev3dev PUBLIC 25 | $ 26 | $ 27 | ) 28 | 29 | target_compile_definitions(ev3dev PUBLIC 30 | _GLIBCXX_USE_NANOSLEEP 31 | EV3DEV_PLATFORM_${EV3DEV_PLATFORM} 32 | ) 33 | target_link_libraries(ev3dev PUBLIC pthread) 34 | 35 | function(add_ev3_executable target sources) 36 | add_executable(${target} ${sources}) 37 | target_link_libraries(${target} ev3dev) 38 | endfunction() 39 | 40 | if (EV3DEV_LANG_CPP_MASTER_PROJECT) 41 | enable_testing() 42 | add_subdirectory(tests) 43 | 44 | add_subdirectory(demos) 45 | 46 | #---------------------------------------------------------------------- 47 | # Install the library, header, and cmake configuration 48 | #---------------------------------------------------------------------- 49 | install(FILES ev3dev.h DESTINATION include) 50 | install(TARGETS ev3dev EXPORT ev3devTargets 51 | LIBRARY DESTINATION lib 52 | ARCHIVE DESTINATION lib 53 | INCLUDES DESTINATION include 54 | ) 55 | 56 | configure_file( 57 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ev3dev-config.cmake" 58 | "${CMAKE_CURRENT_BINARY_DIR}/cmake/ev3dev-config.cmake" 59 | COPYONLY 60 | ) 61 | 62 | export(EXPORT ev3devTargets 63 | FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/ev3dev-targets.cmake" 64 | NAMESPACE ev3dev:: 65 | ) 66 | 67 | export(PACKAGE ev3dev) 68 | 69 | install(EXPORT ev3devTargets 70 | FILE ev3dev-targets.cmake 71 | NAMESPACE ev3dev:: 72 | DESTINATION share/ev3dev/cmake 73 | ) 74 | 75 | install( 76 | FILES cmake/ev3dev-config.cmake 77 | DESTINATION share/ev3dev/cmake 78 | ) 79 | endif() 80 | 81 | 82 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License 2 | =============== 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C++ language bindings for ev3dev 2 | 3 | [![Build Status](https://travis-ci.org/ddemidov/ev3dev-lang-cpp.svg?branch=master)](https://travis-ci.org/ddemidov/ev3dev-lang-cpp) 4 | 5 | ## Compiling 6 | 7 | * EV3: 8 | ``` 9 | mkdir build 10 | cd build 11 | cmake .. -DEV3DEV_PLATFORM=EV3 12 | make 13 | ``` 14 | 15 | * BrickPi: 16 | ``` 17 | mkdir build 18 | cd build 19 | cmake .. -DEV3DEV_PLATFORM=RPI 20 | make 21 | ``` 22 | 23 | You have several options for compiling. 24 | 25 | ## Cross-compiling 26 | 27 | You can use a cross-compiling toolchain to create ARM compatible code. Note: 28 | You need a Linux toolchain, not a "bare-metal" toolchain. If it does not have 29 | "linux" in the name, it probably won't work. 30 | 31 | Pros: Fastest option. Works on Windows and Mac without a virtual machine. 32 | 33 | Cons: Only includes standard libraries - no Debian `-dev` packages. 34 | 35 | ### Windows 36 | 37 | [MentorGraphics toolchain](http://sourcery.mentor.com/public/gnu_toolchain/arm-none-linux-gnueabi/arm-2014.05-29-arm-none-linux-gnueabi.exe) (formerly known as CodeSourcery). 38 | 39 | ### Windows 10 40 | 41 | Windows 10 supports the Windows Subsystem for Linux which allows us to install and execute the entire compiler toolchain. The steps required to compile native-mode EV3 applications on Windows 10 is as follows: 42 | 43 | 1. Be sure that your Windows 10 installation has Windows Subsystem for Linux installed. First make sure Developer Mode is enabled under Settings --> Update & Security --> For developers. Then, go to Control Panel --> Programs and Features --> Turn Windows Feature On/Off and check the box next to Windows Subsystem for Linux (Beta). 44 | 45 | 2. Fire up the bash shell by pressing Start Key, type `bash` and press `Enter`. This will open up Bash on Ubuntu on Windows. 46 | 47 | 3. Install the ARM compiler by typing `sudo apt-get install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi`. 48 | 49 | 4. Use `git clone` to clone this repository to the directory of your choice, e.g., 50 | 51 | ```sh 52 | git clone https://github.com/ddemidov/ev3dev-lang-cpp.git 53 | ``` 54 | 55 | will clone the repo into a directory called `/ev3dev-lang-cpp.git`. 56 | 57 | 5. You need to make some changes to the top-level `CMakeLists.txt` file. First, go into the directory 58 | 59 | ```sh 60 | cd ev3dev-lang-cpp 61 | ``` 62 | Now, edit the `CMakeLists.txt` file with a text editor of your choice, e.g., 63 | 64 | ```sh 65 | vi CMakeLists.txt 66 | ``` 67 | 68 | Just after the `project(...)` declaration, set the C/C++ compilers by adding the following lines: 69 | 70 | ```cmake 71 | set(CMAKE_C_COMPILER "arm-linux-gnueabi-gcc") 72 | set(CMAKE_CC_COMPILER "arm-linux-gnueabi-gcc") 73 | set(CMAKE_CXX_COMPILER "arm-linux-gnueabi-g++") 74 | ``` 75 | 76 | Alternatively, you can set these environment variables during compilation (explained later). 77 | 78 | 6. Now compile your programs and the generated binaries will be ready for EV3. This assumes that you have build tools such as `make` and `cmake` installed - if not, install them with `sudo apt-get install build-essential` (for make) and `sudo apt-get install cmake` for cmake. You can then perform compilation by invoking the following commands: 79 | 80 | ```sh 81 | mkdir build && cd build 82 | cmake .. -DEV3DEV_PLATFORM=EV3 83 | make 84 | ``` 85 | 86 | If you did not set the variables in the `CMakeLists.txt` file, use the following commands instead: 87 | 88 | ```sh 89 | mkdir build && cd build 90 | CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabi-g++ cmake .. 91 | make 92 | ``` 93 | 94 | 7. Install `gcc-5` from `unstable` on your EV3, or else files compiled in Windows will not execute. First, on the EV3, edit `/etc/apt/sources.list` to allow searching for packages in `unstable`: 95 | 96 | ``` 97 | sudo vim /etc/apt/sources.list 98 | ``` 99 | Append the following lines to the end of the file: 100 | ``` 101 | deb http://httpredir.debian.org/debian unstable main contrib non-free 102 | deb http://security.debian.org/ unstable/updates main contrib non-free 103 | deb http://archive.ev3dev.org/debian unstable main 104 | ``` 105 | 106 | Save and exit. Afterwards, run `sudo apt-get update`, which will update the packages searched for. Finally, install the new gcc compiler: 107 | 108 | ``` 109 | sudo apt-get install gcc-5/unstable 110 | ``` 111 | 112 | 8. The `build` directory will now contain folders with binary files ready to be executed on the EV3 brick. The easiest way to copy files is to use a program that supports SFTP, such as Filezilla. Remember that, by default, the username of the host system is `robot` and password is `maker`. The location of the path where the files are kept on disk is likely the following: 113 | 114 | ``` 115 | c:\users\\appdata\local\lxss\home\\ev3dev-lang-cpp\build\ 116 | ``` 117 | 118 | Alternatively, Secure Copy may be used to quickly transfer a few files without having to leave the shell: 119 | 120 | ``` 121 | scp file.txt @:/destination/directory 122 | ``` 123 | 124 | 9. Be sure to `chmod u+x myprogram` for every copied program before running the program, otherwise you'll get an `Access Denied` in SSH or some really weird error if executing from the brick. 125 | 126 | ### Mac 127 | 128 | The 129 | [Carlson-Minot toolchain](http://www.carlson-minot.com/available-arm-gnu-linux-g-lite-builds-for-mac-os-x/mac-os-x-arm-gnu-linux-g-lite-201405-29-toolchain) 130 | provides a complete toolchain for cross-compilation on the Mac. Download the "ARM GNU/Linux G++ Lite 2014.05-29 Easy Installer" and run it. 131 | 132 | To get the cross-compilation working, use (replace EV3 by RPI for the BrickPi) 133 | ``` 134 | mkdir build 135 | cd build 136 | CC=arm-none-linux-gnueabi-gcc CXX=arm-none-linux-gnueabi-g++ cmake -DCMAKE_SYSTEM_NAME=Linux -DEV3DEV_PLATFORM=EV3 .. 137 | make 138 | ``` 139 | 140 | ## Brickstrap 141 | 142 | Brickstrap uses QEMU to create a virtual environment (not exactly a virtual 143 | machine) that can run the same ARM compatible code on a different type of 144 | computer. 145 | 146 | Pros: Faster than running on the EV3 itself. Can install all Debian `-dev` 147 | packages using `apt-get`. 148 | 149 | Cons: Slower than cross-compiler. Requires Linux (Ubuntu). 150 | 151 | See [this page](https://github.com/ev3dev/ev3dev/wiki/Using-brickstrap-to-cross-compile-and-debug) for instructions. 152 | 153 | 154 | ## On the Brick 155 | 156 | It is possible to compile programs on the EV3 brick itself. 157 | 158 | Pros: Easy to setup. 159 | 160 | Cons: Really slow. 161 | 162 | Just run `sudo apt-get install build-essential` on the EV3 and you will have 163 | everything you need. 164 | -------------------------------------------------------------------------------- /autogen-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "ev3dev.h", 4 | "ev3dev.cpp", 5 | "demos/ev3dev-lang-test.cpp", 6 | "demos/ev3dev-lang-demo.cpp" 7 | ], 8 | "templateDir": "templates" 9 | } 10 | -------------------------------------------------------------------------------- /cmake/ev3dev-config.cmake: -------------------------------------------------------------------------------- 1 | # ev3dev-config.cmake - CMake configuration file for external 2 | # projects. 3 | # 4 | # Use this by invoking 5 | # 6 | # find_package(ev3dev) 7 | # 8 | # followed by 9 | # 10 | # add_executable(myrobot myrobot.cpp) 11 | # target_link_libraries(myrobot ev3dev::ev3dev) 12 | # 13 | # The module defines ev3dev::ev3dev IMPORTED target that takes care of proper 14 | # compile and link options. 15 | 16 | include("${CMAKE_CURRENT_LIST_DIR}/ev3dev-targets.cmake") 17 | message(STATUS "Found ev3dev") 18 | -------------------------------------------------------------------------------- /demos/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_ev3_executable(button-test button-test.cpp) 2 | add_ev3_executable(drive-test drive-test.cpp) 3 | add_ev3_executable(ev3dev-lang-demo ev3dev-lang-demo.cpp) 4 | add_ev3_executable(ev3dev-lang-test ev3dev-lang-test.cpp) 5 | add_ev3_executable(remote-control remote-control.cpp) 6 | add_ev3_executable(remote_control-test remote_control-test.cpp) 7 | add_ev3_executable(sound-test sound-test.cpp) 8 | -------------------------------------------------------------------------------- /demos/README.md: -------------------------------------------------------------------------------- 1 | The examples in this folder, unless stated otherwise, are based on Explor3r 2 | robot by Laurens Valk. The assembling instructions for the robot may be found 3 | here: http://robotsquare.com/2015/10/06/explor3r-building-instructions. 4 | -------------------------------------------------------------------------------- /demos/button-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "ev3dev.h" 5 | 6 | using namespace std; 7 | using namespace ev3dev; 8 | 9 | int 10 | main () 11 | { 12 | bool up = false, down = false, left = false, right = false, enter = false, 13 | escape = false; 14 | 15 | while (escape == 0) 16 | { 17 | up = button::up.pressed (); 18 | down = button::down.pressed (); 19 | left = button::left.pressed (); 20 | right = button::right.pressed (); 21 | enter = button::enter.pressed (); 22 | escape = button::back.pressed (); 23 | 24 | printf ("up:%d down:%d left:%d right:%d enter:%d esc:%d\n", up, down, 25 | left, right, enter, escape); 26 | usleep (100000); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /demos/drive-test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 - Franz Detro 3 | * 4 | * Some real world test program for motor control 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #include "ev3dev.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #ifndef NO_LINUX_HEADERS 29 | #include 30 | #include 31 | #include 32 | #define KEY_RELEASE 0 33 | #define KEY_PRESS 1 34 | #define KEY_REPEAT 2 35 | #endif 36 | 37 | using namespace std; 38 | using namespace ev3dev; 39 | 40 | class control 41 | { 42 | public: 43 | control(); 44 | ~control(); 45 | 46 | void drive(int speed, int time=0); 47 | void turn(int direction); 48 | void stop(); 49 | void reset(); 50 | 51 | bool initialized() const; 52 | 53 | void terminate_on_key(); 54 | void panic_if_touched(); 55 | 56 | void remote_loop(); 57 | void drive_autonomously(); 58 | 59 | void terminate() { _terminate = true; } 60 | 61 | protected: 62 | large_motor _motor_left; 63 | large_motor _motor_right; 64 | infrared_sensor _sensor_ir; 65 | touch_sensor _sensor_touch; 66 | 67 | enum state 68 | { 69 | state_idle, 70 | state_driving, 71 | state_turning 72 | }; 73 | 74 | state _state; 75 | bool _terminate; 76 | }; 77 | 78 | control::control() : 79 | _motor_left(OUTPUT_B), 80 | _motor_right(OUTPUT_C), 81 | _state(state_idle), 82 | _terminate(false) 83 | { 84 | } 85 | 86 | control::~control() 87 | { 88 | reset(); 89 | } 90 | 91 | void control::drive(int speed, int time) 92 | { 93 | _motor_left.set_speed_sp(-speed); 94 | 95 | _motor_right.set_speed_sp(-speed); 96 | 97 | _state = state_driving; 98 | 99 | if (time > 0) 100 | { 101 | _motor_left .set_time_sp(time).run_timed(); 102 | _motor_right.set_time_sp(time).run_timed(); 103 | 104 | while (_motor_left.state().count("running") || _motor_right.state().count("running")) 105 | this_thread::sleep_for(chrono::milliseconds(10)); 106 | 107 | _state = state_idle; 108 | } 109 | else 110 | { 111 | _motor_left.run_forever(); 112 | _motor_right.run_forever(); 113 | } 114 | } 115 | 116 | void control::turn(int direction) 117 | { 118 | if (_state != state_idle) 119 | stop(); 120 | 121 | if (direction == 0) 122 | return; 123 | 124 | _state = state_turning; 125 | 126 | _motor_left. set_position_sp( direction).set_speed_sp(500).run_to_rel_pos(); 127 | _motor_right.set_position_sp(-direction).set_speed_sp(500).run_to_rel_pos(); 128 | 129 | while (_motor_left.state().count("running") || _motor_right.state().count("running")) 130 | this_thread::sleep_for(chrono::milliseconds(10)); 131 | 132 | _state = state_idle; 133 | } 134 | 135 | void control::stop() 136 | { 137 | _motor_left .stop(); 138 | _motor_right.stop(); 139 | 140 | _state = state_idle; 141 | } 142 | 143 | void control::reset() 144 | { 145 | if (_motor_left.connected()) 146 | _motor_left.reset(); 147 | 148 | if (_motor_right.connected()) 149 | _motor_right.reset(); 150 | 151 | _state = state_idle; 152 | } 153 | 154 | bool control::initialized() const 155 | { 156 | return (_motor_left .connected() && 157 | _motor_right.connected() && 158 | _sensor_ir .connected()); 159 | } 160 | 161 | void control::terminate_on_key() 162 | { 163 | #ifndef NO_LINUX_HEADERS 164 | thread t([&] () { 165 | int fd = open("/dev/input/by-path/platform-gpio-keys.0-event", O_RDONLY); 166 | if (fd < 0) 167 | { 168 | cout << "Couldn't open platform-gpio-keys device!" << endl; 169 | return; 170 | } 171 | 172 | input_event ev; 173 | while (true) 174 | { 175 | size_t rb = read(fd, &ev, sizeof(ev)); 176 | 177 | if (rb < sizeof(input_event)) 178 | continue; 179 | 180 | if ((ev.type == EV_KEY) /*&& (ev.value == KEY_PRESS)*/) 181 | { 182 | terminate(); 183 | return; 184 | } 185 | } 186 | }); 187 | t.detach(); 188 | #endif 189 | } 190 | 191 | void control::panic_if_touched() 192 | { 193 | if (!_sensor_touch.connected()) 194 | { 195 | cout << "no touch sensor found!" << endl; 196 | return; 197 | } 198 | 199 | thread t([&] () { 200 | while (!_terminate) { 201 | if (_sensor_touch.value()) 202 | { 203 | terminate(); 204 | reset(); 205 | break; 206 | } 207 | this_thread::sleep_for(chrono::milliseconds(100)); 208 | } 209 | }); 210 | t.detach(); 211 | } 212 | 213 | void control::remote_loop() 214 | { 215 | remote_control r(_sensor_ir); 216 | 217 | if (!r.connected()) 218 | { 219 | cout << "no infrared sensor found!" << endl; 220 | return; 221 | } 222 | 223 | const int speed = 700; 224 | const int ninety_degrees = 260; 225 | 226 | r.on_red_up = [&] (bool state) 227 | { 228 | if (state) 229 | { 230 | if (_state == state_idle) 231 | drive(speed); 232 | } 233 | else 234 | stop(); 235 | }; 236 | 237 | r.on_red_down = [&] (bool state) 238 | { 239 | if (state) 240 | { 241 | if (_state == state_idle) 242 | drive(-speed); 243 | } 244 | else 245 | stop(); 246 | }; 247 | 248 | r.on_blue_up = [&] (bool state) 249 | { 250 | if (state) 251 | { 252 | if (_state == state_idle) 253 | turn(-ninety_degrees); 254 | } 255 | }; 256 | 257 | r.on_blue_down = [&] (bool state) 258 | { 259 | if (state) 260 | { 261 | if (_state == state_idle) 262 | turn(ninety_degrees); 263 | } 264 | }; 265 | 266 | r.on_beacon = [&] (bool state) 267 | { 268 | if (state) 269 | terminate(); 270 | }; 271 | 272 | while (!_terminate) 273 | { 274 | if (!r.process()) 275 | { 276 | this_thread::sleep_for(chrono::milliseconds(10)); 277 | } 278 | } 279 | 280 | reset(); 281 | } 282 | 283 | void control::drive_autonomously() 284 | { 285 | if (!_sensor_ir.connected()) 286 | { 287 | cout << "no infrared sensor found!" << endl; 288 | return; 289 | } 290 | 291 | _sensor_ir.set_mode(infrared_sensor::mode_ir_prox); 292 | 293 | while (!_terminate) 294 | { 295 | int distance = _sensor_ir.value(); 296 | if (distance <= 0) 297 | { 298 | // panic 299 | terminate(); 300 | reset(); 301 | break; 302 | } 303 | else if (distance >= 20) 304 | { 305 | if (_state != state_driving) 306 | drive(750); 307 | this_thread::sleep_for(chrono::milliseconds(10)); 308 | } 309 | else 310 | { 311 | stop(); 312 | 313 | int direction = 100; 314 | int start_distance = distance; 315 | 316 | while (distance <= 40) 317 | { 318 | turn(direction); 319 | 320 | distance = _sensor_ir.value(); 321 | if (distance < start_distance) 322 | { 323 | if (direction < 0) 324 | { 325 | drive(-700, 1000); 326 | } 327 | else 328 | { 329 | direction = -200; 330 | } 331 | } 332 | } 333 | } 334 | } 335 | } 336 | 337 | int main() 338 | { 339 | control c; 340 | 341 | if (c.initialized()) 342 | { 343 | c.terminate_on_key(); // we terminate if a button is pressed 344 | c.panic_if_touched(); // we panic if the touch sensor is triggered 345 | 346 | // change mode to 1 to get IR remote mode 347 | int mode = 2; 348 | if (mode == 1) 349 | { 350 | cout << "ensure that channel 1 is selected on your remote control." << endl << endl 351 | << "upper red button - forward" << endl 352 | << "lower red button - backward" << endl 353 | << "upper blue button - left" << endl 354 | << "lower blue button - right" << endl 355 | << "middle button - exit" << endl << endl; 356 | 357 | c.remote_loop(); 358 | } 359 | else if (mode == 2) 360 | { 361 | cout << "touch the sensor or press a button to stop." << endl << endl; 362 | 363 | c.drive_autonomously(); 364 | } 365 | } 366 | else 367 | { 368 | cout << "you need to connect an infrared sensor and large motors to ports B and C!" << endl; 369 | return 1; 370 | } 371 | 372 | return 0; 373 | } 374 | -------------------------------------------------------------------------------- /demos/ev3dev-lang-demo.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * demo program for the ev3dev C++ binding 3 | * 4 | * Copyright (c) 2014 - Franz Detro 5 | * 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | */ 21 | 22 | #include "ev3dev.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | using namespace std; 30 | using namespace ev3dev; 31 | 32 | std::ostream& operator<<(std::ostream &os, const std::set &ss) { 33 | os << "[ "; 34 | for(const auto &s : ss) os << s << " "; 35 | return os << "]"; 36 | } 37 | 38 | 39 | void print_values(sensor &s) 40 | { 41 | auto dp = s.decimals(); 42 | unsigned m = s.num_values(); 43 | for (unsigned i=0; i> c; 71 | 72 | switch (c) 73 | { 74 | case 's': 75 | { 76 | cout << "available modes are "; 77 | const mode_set &m = s.modes(); 78 | for (mode_set::const_iterator it = m.begin(); it!=m.end(); ++it) 79 | { 80 | cout << *it << " "; 81 | } 82 | cout << endl; 83 | } 84 | break; 85 | case 'c': 86 | { 87 | string mode; 88 | cout << endl << "new mode: "; cin >> mode; cout << endl; 89 | s.set_mode(mode); 90 | } 91 | break; 92 | case 'v': 93 | cout << endl << "value is "; print_values(s); cout << endl; 94 | break; 95 | case 'm': 96 | { 97 | bool bStop = false; 98 | thread t([&] () { 99 | char cc; cin >> cc; 100 | bStop = true; 101 | }); 102 | 103 | int value, lastValue = -99999; 104 | while (!bStop) 105 | { 106 | value = s.value(); 107 | if (value != lastValue) 108 | { 109 | lastValue = value; 110 | print_values(s); 111 | cout << endl; 112 | } 113 | this_thread::sleep_for(chrono::milliseconds(100)); 114 | } 115 | t.join(); 116 | } 117 | break; 118 | } 119 | } 120 | while (c != 'b'); 121 | } 122 | 123 | void sensor_menu() 124 | { 125 | sensor arrSensors[4] { 126 | { INPUT_1 }, 127 | { INPUT_2 }, 128 | { INPUT_3 }, 129 | { INPUT_4 } 130 | }; 131 | 132 | char c = 0; 133 | do 134 | { 135 | cout << endl 136 | << "*** sensor menu ***" << endl 137 | << endl; 138 | 139 | for (unsigned i=0; i<4; ++i) 140 | { 141 | sensor &s = arrSensors[i]; 142 | if (s.connected()) 143 | { 144 | cout << "(" << i+1 << ") " << s.type_name() << " (device " << s.driver_name() 145 | << ", port " << s.address() << ", mode " << s.mode() << ")" << endl; 146 | } 147 | } 148 | cout << endl; 149 | cout << "(b)ack" << endl; 150 | cout << endl 151 | << "Choice: "; 152 | cin >> c; 153 | 154 | switch (c) 155 | { 156 | case '1': 157 | case '2': 158 | case '3': 159 | case '4': 160 | sensor_action(arrSensors[c-'1']); 161 | break; 162 | } 163 | } 164 | while (c != 'b'); 165 | } 166 | 167 | void motor_action(motor &dev) 168 | { 169 | char c = 0; 170 | int new_value = 0; 171 | std::string answer; 172 | bool running = false; 173 | 174 | do 175 | { 176 | cout << endl 177 | << "*** " << dev.driver_name() << " motor (" << dev.address() << ") actions ***" << endl 178 | << endl 179 | << "(i)nfo" << endl 180 | << "(c)ommand" << endl 181 | << "st(o)p command [" << dev.stop_action() << "]" << endl 182 | << "speed (s)etpoint (" << dev.speed_sp() << ")" << endl; 183 | 184 | cout << "(p)osition setpoint (" << dev.position_sp() << ")" << endl 185 | << "ramp (u)p setpoint (" << dev.ramp_up_sp() << ")" << endl 186 | << "ramp (d)own setpoint (" << dev.ramp_down_sp() << ")" << endl 187 | << "(t)ime setpoint (" << dev.time_sp() << ")" << endl 188 | << endl 189 | << "(0) reset position" << endl 190 | << endl 191 | << "(b)ack" << endl 192 | << endl 193 | << "Choice: "; 194 | cin >> c; 195 | 196 | switch (c) 197 | { 198 | case 'i': 199 | cout << endl; 200 | cout << " Address: "; 201 | try { cout << dev.address() << endl; } 202 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 203 | cout << " Commands: "; 204 | try { cout << dev.commands() << endl; } 205 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 206 | cout << " Count Per Rot: "; 207 | try { cout << dev.count_per_rot() << endl; } 208 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 209 | cout << " Count Per M: "; 210 | try { cout << dev.count_per_m() << endl; } 211 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 212 | cout << " Driver Name: "; 213 | try { cout << dev.driver_name() << endl; } 214 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 215 | cout << " Duty Cycle: "; 216 | try { cout << dev.duty_cycle() << endl; } 217 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 218 | cout << " Duty Cycle SP: "; 219 | try { cout << dev.duty_cycle_sp() << endl; } 220 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 221 | cout << " Full Travel Count: "; 222 | try { cout << dev.full_travel_count() << endl; } 223 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 224 | cout << " Polarity: "; 225 | try { cout << dev.polarity() << endl; } 226 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 227 | cout << " Position: "; 228 | try { cout << dev.position() << endl; } 229 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 230 | cout << " Position P: "; 231 | try { cout << dev.position_p() << endl; } 232 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 233 | cout << " Position I: "; 234 | try { cout << dev.position_i() << endl; } 235 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 236 | cout << " Position D: "; 237 | try { cout << dev.position_d() << endl; } 238 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 239 | cout << " Position SP: "; 240 | try { cout << dev.position_sp() << endl; } 241 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 242 | cout << " Max Speed: "; 243 | try { cout << dev.max_speed() << endl; } 244 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 245 | cout << " Speed: "; 246 | try { cout << dev.speed() << endl; } 247 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 248 | cout << " Speed SP: "; 249 | try { cout << dev.speed_sp() << endl; } 250 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 251 | cout << " Ramp Up SP: "; 252 | try { cout << dev.ramp_up_sp() << endl; } 253 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 254 | cout << " Ramp Down SP: "; 255 | try { cout << dev.ramp_down_sp() << endl; } 256 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 257 | cout << " Speed P: "; 258 | try { cout << dev.speed_p() << endl; } 259 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 260 | cout << " Speed I: "; 261 | try { cout << dev.speed_i() << endl; } 262 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 263 | cout << " Speed D: "; 264 | try { cout << dev.speed_d() << endl; } 265 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 266 | cout << " State: "; 267 | try { cout << dev.state() << endl; } 268 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 269 | cout << " Stop Action: "; 270 | try { cout << dev.stop_action() << endl; } 271 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 272 | cout << " Stop Actions: "; 273 | try { cout << dev.stop_actions() << endl; } 274 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 275 | cout << " Time SP: "; 276 | try { cout << dev.time_sp() << endl; } 277 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 278 | cout << endl; 279 | break; 280 | case 'c': 281 | cout << "command " << dev.commands() << ": "; 282 | cin >> answer; dev.set_command(answer); cout << endl; 283 | break; 284 | case 'o': 285 | cout << "stop command " << dev.stop_actions() << ": "; 286 | cin >> answer; dev.set_stop_action(answer); cout << endl; 287 | break; 288 | case 's': 289 | cout << "speed: "; cin >> new_value; dev.set_speed_sp(new_value); cout << endl; 290 | break; 291 | case 'p': 292 | cout << "position: "; cin >> new_value; dev.set_position_sp(new_value); cout << endl; 293 | break; 294 | case 'u': 295 | cout << "ramp up: "; cin >> new_value; dev.set_ramp_up_sp(new_value); cout << endl; 296 | break; 297 | case 'd': 298 | cout << "ramp down: "; cin >> new_value; dev.set_ramp_down_sp(new_value); cout << endl; 299 | break; 300 | case 't': 301 | cout << "time: "; cin >> new_value; dev.set_time_sp(new_value); cout << endl; 302 | break; 303 | case '0': 304 | dev.set_position(0); 305 | break; 306 | } 307 | } 308 | while (c != 'b'); 309 | } 310 | 311 | void motor_action(dc_motor &m) 312 | { 313 | char c = 0; 314 | int new_value = 0; 315 | std::string new_mode; 316 | 317 | do 318 | { 319 | cout << endl 320 | << "*** dc motor (" << m.address() << ") actions ***" << endl 321 | << endl 322 | << "(d)uty cycle sp(" << m.duty_cycle_sp() << ")" << endl 323 | << "(r)amp down sp (" << m.ramp_down_sp() << ")" << endl 324 | << "ramp (u)p sp (" << m.ramp_up_sp() << ")" << endl 325 | << "p(o)larity [" << m.polarity() << "]" << endl; 326 | cout << endl << "(b)ack" << endl 327 | << endl 328 | << "Choice: "; 329 | cin >> c; 330 | 331 | switch (c) 332 | { 333 | case 'c': 334 | cout << "command " << m.commands() << ": "; 335 | cin >> new_mode; m.set_command(new_mode); cout << endl; 336 | break; 337 | case 'o': 338 | cout << "polarity (normal, inverted): "; 339 | cin >> new_mode; m.set_polarity(new_mode); cout << endl; 340 | break; 341 | case 'd': 342 | cout << "duty cycle sp: "; cin >> new_value; m.set_duty_cycle_sp(new_value); cout << endl; 343 | break; 344 | case 'r': 345 | cout << "ramp down sp: "; cin >> new_value; m.set_ramp_down_sp(new_value); cout << endl; 346 | break; 347 | case 'u': 348 | cout << "ramp up sp: "; cin >> new_value; m.set_ramp_up_sp(new_value); cout << endl; 349 | break; 350 | } 351 | } 352 | while (c != 'b'); 353 | } 354 | 355 | void motor_action(servo_motor &m) 356 | { 357 | char c = 0; 358 | int new_value = 0; 359 | std::string new_mode; 360 | 361 | do 362 | { 363 | cout << endl 364 | << "*** servo motor (" << m.address() << ") actions ***" << endl 365 | << endl 366 | << "(c)ommand" << endl 367 | << "(p)osition_sp (" << m.position_sp() << ")" << endl 368 | << "(r)ate_sp (" << m.rate_sp() << ")" << endl 369 | << "mi(n) pulse sp (" << m.min_pulse_sp() << ")" << endl 370 | << "mi(d) pulse sp (" << m.mid_pulse_sp() << ")" << endl 371 | << "ma(x) pulse sp (" << m.max_pulse_sp() << ")" << endl 372 | << "p(o)larity [" << m.polarity() << "]" << endl; 373 | cout << endl << "(b)ack" << endl 374 | << endl 375 | << "Choice: "; 376 | cin >> c; 377 | 378 | switch (c) 379 | { 380 | case 'c': 381 | cout << "command (run, float): "; 382 | cin >> new_mode; m.set_command(new_mode); cout << endl; 383 | break; 384 | case 'p': 385 | cout << "position sp: "; cin >> new_value; m.set_position_sp(new_value); cout << endl; 386 | break; 387 | case 'o': 388 | cout << "polarity (normal, inverted): "; 389 | cin >> new_mode; m.set_polarity(new_mode); cout << endl; 390 | break; 391 | case 'r': 392 | cout << "rate sp: "; cin >> new_value; m.set_rate_sp(new_value); cout << endl; 393 | break; 394 | case 'n': 395 | cout << "min pulse sp: "; cin >> new_value; m.set_min_pulse_sp(new_value); cout << endl; 396 | break; 397 | case 'd': 398 | cout << "mid pulse sp: "; cin >> new_value; m.set_mid_pulse_sp(new_value); cout << endl; 399 | break; 400 | case 'x': 401 | cout << "max pulse sp: "; cin >> new_value; m.set_max_pulse_sp(new_value); cout << endl; 402 | break; 403 | } 404 | } 405 | while (c != 'b'); 406 | } 407 | 408 | void motor_menu() 409 | { 410 | motor arrMotors[4] = { 411 | { OUTPUT_A }, 412 | { OUTPUT_B }, 413 | { OUTPUT_C }, 414 | { OUTPUT_D } 415 | }; 416 | dc_motor arrDCMotors[4] = { 417 | { OUTPUT_A }, 418 | { OUTPUT_B }, 419 | { OUTPUT_C }, 420 | { OUTPUT_D } 421 | }; 422 | servo_motor arrServoMotors[4] = { 423 | { OUTPUT_A }, 424 | { OUTPUT_B }, 425 | { OUTPUT_C }, 426 | { OUTPUT_D } 427 | }; 428 | 429 | char c = 0; 430 | do 431 | { 432 | cout << endl 433 | << "*** motor menu ***" << endl 434 | << endl; 435 | 436 | for (unsigned i=0; i<4; ++i) 437 | { 438 | motor &m = arrMotors[i]; 439 | if (m.connected()) 440 | { 441 | cout << "(" << 1+i << ") " << m.driver_name() << " motor on port " << m.address() << endl; 442 | } 443 | else if (arrDCMotors[i].connected()) 444 | { 445 | cout << "(" << 1+i << ") dc motor on port " << arrDCMotors[i].address() << endl; 446 | } 447 | else if (arrServoMotors[i].connected()) 448 | { 449 | cout << "(" << 1+i << ") servo motor on port " << arrServoMotors[i].address() << endl; 450 | } 451 | } 452 | cout << endl; 453 | cout << "(b)ack" << endl; 454 | cout << endl 455 | << "Choice: "; 456 | cin >> c; 457 | 458 | switch (c) 459 | { 460 | case '1': 461 | case '2': 462 | case '3': 463 | case '4': 464 | if (arrMotors[c-'1'].connected()) 465 | motor_action(arrMotors[c-'1']); 466 | else if (arrMotors[c-'1'].connected()) 467 | motor_action(arrDCMotors[c-'1']); 468 | else if (arrServoMotors[c-'1'].connected()) 469 | motor_action(arrServoMotors[c-'1']); 470 | break; 471 | } 472 | } 473 | while (c != 'b'); 474 | } 475 | 476 | void led_action(const char *name, led &l) 477 | { 478 | int interval = 500; 479 | char c = 0; 480 | do 481 | { 482 | cout << endl 483 | << "*** " << name << " actions ***" << endl 484 | << endl 485 | << "(0) off" << endl 486 | << "(1) on" << endl 487 | << "(f)lash" << endl 488 | << "(i)nterval" << endl 489 | << "(t)rigger" << endl 490 | << endl 491 | << "(b)ack" << endl 492 | << endl 493 | << "Choice: "; 494 | cin >> c; 495 | 496 | switch (c) 497 | { 498 | case '0': 499 | l.off(); 500 | break; 501 | case '1': 502 | l.on(); 503 | break; 504 | case 'f': 505 | l.flash(200, 200); 506 | break; 507 | case 'i': 508 | cout << "interval: "; cin >> interval; cout << endl; 509 | l.set_delay_on(interval); l.set_delay_off(interval); 510 | break; 511 | case 't': 512 | { 513 | cout << "available triggers are " << endl; 514 | mode_type t = l.trigger(); 515 | mode_set s = l.triggers(); 516 | for (mode_set::const_iterator it = s.begin(); it!=s.end(); ++it) 517 | { 518 | if (*it == t) 519 | cout << "[" << *it << "] "; 520 | else 521 | cout << *it << " "; 522 | } 523 | cout << endl << endl << "choice: "; 524 | 525 | cin >> t; 526 | if (!t.empty()) 527 | l.set_trigger(t); 528 | } 529 | break; 530 | } 531 | } 532 | while (c != 'b'); 533 | } 534 | 535 | void led_menu() 536 | { 537 | led arrPortLEDs[4] { 538 | { "ev3::outA" }, { "ev3::outB" }, { "ev3::outC" }, { "ev3::outD" } }; 539 | 540 | char c = 0; 541 | do 542 | { 543 | cout << endl 544 | << "*** led menu ***" << endl 545 | << endl 546 | << "(1) green:left" << endl 547 | << "(2) green:right" << endl 548 | << "(3) red:left" << endl 549 | << "(4) red:right" << endl; 550 | 551 | for (unsigned i=0; i<4; ++i) 552 | { 553 | if (arrPortLEDs[i].connected()) 554 | { 555 | cout << "(" << 5+i << ") out" << static_cast('A'+i) << endl; 556 | } 557 | } 558 | 559 | cout << endl 560 | << "(b)ack" << endl 561 | << endl 562 | << "Choice: "; 563 | cin >> c; 564 | 565 | switch (c) 566 | { 567 | case '1': 568 | led_action("green:left", led::green_left); 569 | break; 570 | case '2': 571 | led_action("green:right", led::green_right); 572 | break; 573 | case '3': 574 | led_action("red:left", led::red_left); 575 | break; 576 | case '4': 577 | led_action("red:right", led::red_right); 578 | break; 579 | case '5': 580 | led_action("outA", arrPortLEDs[0]); 581 | break; 582 | case '6': 583 | led_action("outB", arrPortLEDs[1]); 584 | break; 585 | case '7': 586 | led_action("outC", arrPortLEDs[2]); 587 | break; 588 | case '8': 589 | led_action("outD", arrPortLEDs[3]); 590 | break; 591 | } 592 | } 593 | while (c != 'b'); 594 | } 595 | 596 | void button_menu() 597 | { 598 | char c = 0; 599 | do 600 | { 601 | cout << endl 602 | << "*** button menu ***" << endl 603 | << endl 604 | << "(1) back" << endl 605 | << "(2) left" << endl 606 | << "(3) right" << endl 607 | << "(4) up" << endl 608 | << "(5) down" << endl 609 | << "(6) enter" << endl 610 | << endl 611 | << "(b)ack" << endl 612 | << endl 613 | << "Choice: "; 614 | cin >> c; 615 | 616 | switch (c) 617 | { 618 | case '1': 619 | cout << endl << "back button is " << (button::back.pressed() ? "down" : "up") << endl; 620 | break; 621 | case '2': 622 | cout << endl << "left button is " << (button::left.pressed() ? "down" : "up") << endl; 623 | break; 624 | case '3': 625 | cout << endl << "right button is " << (button::right.pressed() ? "down" : "up") << endl; 626 | break; 627 | case '4': 628 | cout << endl << "up button is " << (button::up.pressed() ? "down" : "up") << endl; 629 | break; 630 | case '5': 631 | cout << endl << "down button is " << (button::down.pressed() ? "down" : "up") << endl; 632 | break; 633 | case '6': 634 | cout << endl << "enter button is " << (button::enter.pressed() ? "down" : "up") << endl; 635 | break; 636 | } 637 | } 638 | while (c != 'b'); 639 | } 640 | 641 | void sound_menu() 642 | { 643 | unsigned frequency = 440; // 440 Hz 644 | unsigned duration = 1000; // 1 sec 645 | char c = 0; 646 | do 647 | { 648 | cout << endl 649 | << "*** sound menu ***" << endl 650 | << endl 651 | << "b(e)ep" << endl 652 | << "(t)one" << endl 653 | << "tone (d)uration" << endl 654 | << "(p)lay" << endl 655 | << "(s)peak" << endl 656 | << "(v)olume" << endl 657 | << "(b)ack" << endl 658 | << endl 659 | << "Choice: "; 660 | cin >> c; 661 | 662 | switch (c) 663 | { 664 | case 'e': 665 | sound::beep(); 666 | break; 667 | case 't': 668 | cout << "frequency: "; cin >> frequency; cout << endl; 669 | sound::tone(frequency, duration); 670 | break; 671 | case 'd': 672 | cout << "duration: "; cin >> duration; cout << endl; 673 | sound::tone(frequency, duration); 674 | break; 675 | case 'p': 676 | { 677 | std::string fname; 678 | cout << "soundfile: "; cin >> fname; cout << endl; 679 | sound::play(fname, true); 680 | } 681 | break; 682 | case 's': 683 | { 684 | std::string text; 685 | cout << "text: "; getline(cin, text); getline(cin, text); cout << endl; 686 | sound::speak(text, true); 687 | } 688 | break; 689 | } 690 | } 691 | while (c != 'b'); 692 | } 693 | 694 | void battery_menu() 695 | { 696 | char c = 0; 697 | do 698 | { 699 | cout << endl 700 | << "*** battery menu ***" << endl 701 | << endl 702 | << "(v)oltage" << endl 703 | << "(c)urrent" << endl 704 | << endl 705 | << "(b)ack" << endl 706 | << endl 707 | << "Choice: "; 708 | cin >> c; 709 | 710 | switch (c) 711 | { 712 | case 'v': 713 | cout << endl << "voltage is " << power_supply::battery.measured_volts() << " Volt" << endl << endl; 714 | break; 715 | case 'c': 716 | cout << endl << "current is " << power_supply::battery.measured_amps() << " A" << endl << endl; 717 | break; 718 | } 719 | } 720 | while (c != 'b'); 721 | } 722 | 723 | void lcd_menu() 724 | { 725 | lcd l; 726 | 727 | if (!l.available()) 728 | { 729 | cout << endl 730 | << "###error: lcd not available ###" << endl; 731 | return; 732 | } 733 | 734 | char c = 0; 735 | do 736 | { 737 | cout << endl 738 | << "*** lcd menu ***" << endl 739 | << endl 740 | << "(i)nfo" << endl 741 | << "(f)ill" << endl 742 | << "(c)lear" << endl 743 | << endl 744 | << "(b)ack" << endl 745 | << endl 746 | << "Choice: "; 747 | cin >> c; 748 | 749 | switch (c) 750 | { 751 | case 'i': 752 | cout << endl 753 | << "Resolution is " << l.resolution_x() << " x " << l.resolution_y() 754 | << ", " << l.bits_per_pixel() << " bit(s) per pixel" << endl 755 | << "Frame buffer size is " << l.frame_buffer_size() << " byte, " 756 | << "line length is " << l.line_length() << " byte" << endl; 757 | case 'f': 758 | l.fill(0xFF); 759 | break; 760 | case 'c': 761 | l.fill(0); 762 | break; 763 | } 764 | } 765 | while (c != 'b'); 766 | } 767 | 768 | void main_menu() 769 | { 770 | char c = 0; 771 | do 772 | { 773 | cout << endl 774 | << "*** main menu ***" << endl 775 | << endl 776 | << "(s)ensors" << endl 777 | << "(m)otors" << endl 778 | << "(l)eds" << endl 779 | << "(b)uttons" << endl 780 | << "s(o)und" << endl 781 | << "b(a)ttery" << endl 782 | << "l(c)d" << endl 783 | << "(q)uit" << endl 784 | << endl 785 | << "Choice: "; 786 | cin >> c; 787 | 788 | switch (c) 789 | { 790 | case 's': 791 | sensor_menu(); 792 | break; 793 | case 'm': 794 | motor_menu(); 795 | break; 796 | case 'l': 797 | led_menu(); 798 | break; 799 | case 'b': 800 | button_menu(); 801 | break; 802 | case 'o': 803 | sound_menu(); 804 | break; 805 | case 'a': 806 | battery_menu(); 807 | break; 808 | case 'c': 809 | lcd_menu(); 810 | break; 811 | } 812 | } 813 | while (c != 'q'); 814 | } 815 | 816 | int main() 817 | { 818 | main_menu(); 819 | 820 | return 0; 821 | } 822 | -------------------------------------------------------------------------------- /demos/ev3dev-lang-test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * test program for the ev3dev C++ binding 3 | * 4 | * Copyright (c) 2014 - Franz Detro 5 | * 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | */ 21 | 22 | #include "ev3dev.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | using namespace std; 30 | using namespace ev3dev; 31 | 32 | 33 | std::ostream& operator<<(std::ostream &os, const std::set &ss) { 34 | os << "[ "; 35 | for(const auto &s : ss) os << s << " "; 36 | return os << "]"; 37 | } 38 | 39 | template 40 | void test_sensor(const char *name) 41 | { 42 | S dev; 43 | if (dev.connected()) 44 | { 45 | cout << endl 46 | << "Found " << name << " sensor" << endl 47 | << " Current properties are:" << endl; 48 | cout << " Address: "; 49 | try { cout << dev.address() << endl; } 50 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 51 | cout << " Commands: "; 52 | try { cout << dev.commands() << endl; } 53 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 54 | cout << " Decimals: "; 55 | try { cout << dev.decimals() << endl; } 56 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 57 | cout << " Driver Name: "; 58 | try { cout << dev.driver_name() << endl; } 59 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 60 | cout << " Mode: "; 61 | try { cout << dev.mode() << endl; } 62 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 63 | cout << " Modes: "; 64 | try { cout << dev.modes() << endl; } 65 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 66 | cout << " Num Values: "; 67 | try { cout << dev.num_values() << endl; } 68 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 69 | cout << " Units: "; 70 | try { cout << dev.units() << endl; } 71 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 72 | cout << endl; 73 | } 74 | else 75 | cout << "No " << name << " sensor found" << endl; 76 | } 77 | 78 | template 79 | void test_motor(const char *name) 80 | { 81 | M dev; 82 | if (dev.connected()) 83 | { 84 | cout << endl 85 | << "Found " << name << " motor" << endl 86 | << " Current properties are:" << endl; 87 | cout << " Address: "; 88 | try { cout << dev.address() << endl; } 89 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 90 | cout << " Commands: "; 91 | try { cout << dev.commands() << endl; } 92 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 93 | cout << " Count Per Rot: "; 94 | try { cout << dev.count_per_rot() << endl; } 95 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 96 | cout << " Count Per M: "; 97 | try { cout << dev.count_per_m() << endl; } 98 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 99 | cout << " Driver Name: "; 100 | try { cout << dev.driver_name() << endl; } 101 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 102 | cout << " Duty Cycle: "; 103 | try { cout << dev.duty_cycle() << endl; } 104 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 105 | cout << " Duty Cycle SP: "; 106 | try { cout << dev.duty_cycle_sp() << endl; } 107 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 108 | cout << " Full Travel Count: "; 109 | try { cout << dev.full_travel_count() << endl; } 110 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 111 | cout << " Polarity: "; 112 | try { cout << dev.polarity() << endl; } 113 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 114 | cout << " Position: "; 115 | try { cout << dev.position() << endl; } 116 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 117 | cout << " Position P: "; 118 | try { cout << dev.position_p() << endl; } 119 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 120 | cout << " Position I: "; 121 | try { cout << dev.position_i() << endl; } 122 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 123 | cout << " Position D: "; 124 | try { cout << dev.position_d() << endl; } 125 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 126 | cout << " Position SP: "; 127 | try { cout << dev.position_sp() << endl; } 128 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 129 | cout << " Max Speed: "; 130 | try { cout << dev.max_speed() << endl; } 131 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 132 | cout << " Speed: "; 133 | try { cout << dev.speed() << endl; } 134 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 135 | cout << " Speed SP: "; 136 | try { cout << dev.speed_sp() << endl; } 137 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 138 | cout << " Ramp Up SP: "; 139 | try { cout << dev.ramp_up_sp() << endl; } 140 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 141 | cout << " Ramp Down SP: "; 142 | try { cout << dev.ramp_down_sp() << endl; } 143 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 144 | cout << " Speed P: "; 145 | try { cout << dev.speed_p() << endl; } 146 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 147 | cout << " Speed I: "; 148 | try { cout << dev.speed_i() << endl; } 149 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 150 | cout << " Speed D: "; 151 | try { cout << dev.speed_d() << endl; } 152 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 153 | cout << " State: "; 154 | try { cout << dev.state() << endl; } 155 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 156 | cout << " Stop Action: "; 157 | try { cout << dev.stop_action() << endl; } 158 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 159 | cout << " Stop Actions: "; 160 | try { cout << dev.stop_actions() << endl; } 161 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 162 | cout << " Time SP: "; 163 | try { cout << dev.time_sp() << endl; } 164 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 165 | cout << endl; 166 | } 167 | else 168 | cout << "No " << name << " motor found" << endl; 169 | } 170 | 171 | void test_dc_motor() 172 | { 173 | dc_motor dev; 174 | if (dev.connected()) 175 | { 176 | cout << endl 177 | << "Found dc motor" << endl 178 | << " Current properties are:" << endl; 179 | cout << " Address: "; 180 | try { cout << dev.address() << endl; } 181 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 182 | cout << " Commands: "; 183 | try { cout << dev.commands() << endl; } 184 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 185 | cout << " Driver Name: "; 186 | try { cout << dev.driver_name() << endl; } 187 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 188 | cout << " Duty Cycle: "; 189 | try { cout << dev.duty_cycle() << endl; } 190 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 191 | cout << " Duty Cycle SP: "; 192 | try { cout << dev.duty_cycle_sp() << endl; } 193 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 194 | cout << " Polarity: "; 195 | try { cout << dev.polarity() << endl; } 196 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 197 | cout << " Ramp Down SP: "; 198 | try { cout << dev.ramp_down_sp() << endl; } 199 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 200 | cout << " Ramp Up SP: "; 201 | try { cout << dev.ramp_up_sp() << endl; } 202 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 203 | cout << " State: "; 204 | try { cout << dev.state() << endl; } 205 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 206 | cout << " Stop Actions: "; 207 | try { cout << dev.stop_actions() << endl; } 208 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 209 | cout << " Time SP: "; 210 | try { cout << dev.time_sp() << endl; } 211 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 212 | cout << endl; 213 | } 214 | else 215 | cout << "No dc motor found" << endl; 216 | } 217 | 218 | void test_servo_motor() 219 | { 220 | servo_motor dev; 221 | if (dev.connected()) 222 | { 223 | cout << endl 224 | << "Found servo motor" << endl 225 | << " Current properties are:" << endl; 226 | cout << " Address: "; 227 | try { cout << dev.address() << endl; } 228 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 229 | cout << " Driver Name: "; 230 | try { cout << dev.driver_name() << endl; } 231 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 232 | cout << " Max Pulse SP: "; 233 | try { cout << dev.max_pulse_sp() << endl; } 234 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 235 | cout << " Mid Pulse SP: "; 236 | try { cout << dev.mid_pulse_sp() << endl; } 237 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 238 | cout << " Min Pulse SP: "; 239 | try { cout << dev.min_pulse_sp() << endl; } 240 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 241 | cout << " Polarity: "; 242 | try { cout << dev.polarity() << endl; } 243 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 244 | cout << " Position SP: "; 245 | try { cout << dev.position_sp() << endl; } 246 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 247 | cout << " Rate SP: "; 248 | try { cout << dev.rate_sp() << endl; } 249 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 250 | cout << " State: "; 251 | try { cout << dev.state() << endl; } 252 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; } 253 | cout << endl; 254 | } 255 | else 256 | cout << "No servo motor found" << endl; 257 | } 258 | 259 | int main() 260 | { 261 | 262 | test_sensor("touch"); 263 | test_sensor("color"); 264 | test_sensor("ultrasonic"); 265 | test_sensor("gyro"); 266 | test_sensor("infrared"); 267 | test_sensor("i2c"); 268 | 269 | cout << endl; 270 | 271 | test_motor("medium"); 272 | test_motor("large"); 273 | 274 | test_dc_motor(); 275 | test_servo_motor(); 276 | 277 | const vector outPortLEDs { 278 | "ev3::outA", 279 | "ev3::outB", 280 | "ev3::outC", 281 | "ev3::outD" 282 | }; 283 | for (auto const &portName : outPortLEDs) 284 | { 285 | led l { portName }; 286 | if (l.connected()) 287 | { 288 | cout << "Brightness of " << portName << " led is " << l.brightness() << endl; 289 | cout << "Trigger of " << portName << " led is " << l.trigger() << endl << endl; 290 | } 291 | } 292 | 293 | cout << "Brightness of left green led is " << led::green_left.brightness() << endl; 294 | cout << "Trigger of right red led is " << led::red_right.trigger() << endl << endl; 295 | 296 | cout << "Beeping..." << endl << endl; sound::beep(); 297 | 298 | cout << "Battery voltage is " << power_supply::battery.measured_volts() << " V" << endl; 299 | cout << "Battery current is " << power_supply::battery.measured_amps() << " A" << endl; 300 | 301 | cout << endl; 302 | 303 | return 0; 304 | } 305 | -------------------------------------------------------------------------------- /demos/remote-control.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | /* 8 | ----------------------------------------------------------------------------- 9 | Copyright (c) 2015 Denis Demidov 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in 19 | all copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | THE SOFTWARE. 28 | ----------------------------------------------------------------------------- 29 | 30 | This demo shows how to remote control an Explor3r robot with 31 | touch sensor attachment. 32 | 33 | Red buttons control left motor, blue buttons control right motor. 34 | Leds are used to indicate movement direction. 35 | Whenever an obstacle is bumped, robot backs away and apologises. 36 | */ 37 | 38 | namespace ev3 = ev3dev; 39 | 40 | //--------------------------------------------------------------------------- 41 | void precondition(bool cond, const std::string &msg) { 42 | if (!cond) throw std::runtime_error(msg); 43 | } 44 | 45 | //--------------------------------------------------------------------------- 46 | template 47 | std::function roll(Motor &motor, Leds &leds, int dir) { 48 | return [&motor, &leds, dir](bool state) { 49 | if (state) { 50 | motor.set_speed_sp(900 * dir).run_forever(); 51 | ev3::led::set_color(leds, dir > 0 ? ev3::led::green : ev3::led::red); 52 | } else { 53 | motor.set_stop_action("brake").stop(); 54 | for(auto led : leds) led->off(); 55 | } 56 | }; 57 | } 58 | 59 | //--------------------------------------------------------------------------- 60 | int main() { 61 | ev3::large_motor lmotor(ev3::OUTPUT_B); 62 | ev3::large_motor rmotor(ev3::OUTPUT_C); 63 | ev3::remote_control rc; 64 | ev3::touch_sensor ts; 65 | 66 | precondition(lmotor.connected(), "Motor on outB is not connected"); 67 | precondition(rmotor.connected(), "Motor on outC is not connected"); 68 | precondition(rc.connected(), "Infrared sensor is not connected"); 69 | precondition(ts.connected(), "Touch sensor is not connected"); 70 | 71 | rc.on_red_up = roll(lmotor, ev3::led::left, 1); 72 | rc.on_red_down = roll(lmotor, ev3::led::left, -1); 73 | rc.on_blue_up = roll(rmotor, ev3::led::right, 1); 74 | rc.on_blue_down = roll(rmotor, ev3::led::right, -1); 75 | 76 | // Enter event processing loop 77 | while (!ev3::button::enter.pressed()) { 78 | rc.process(); 79 | 80 | // Backup when an obstacle is bumped 81 | if (ts.is_pressed()) { 82 | ev3::sound::speak("Oops, excuse me!"); 83 | 84 | lmotor.set_stop_action("brake").stop(); 85 | rmotor.set_stop_action("brake").stop(); 86 | 87 | // Turn red lights on 88 | ev3::led::set_color(ev3::led::left, ev3::led::red); 89 | ev3::led::set_color(ev3::led::right, ev3::led::red); 90 | 91 | // Run both motors backwards for 0.5 seconds 92 | lmotor.set_time_sp(500).set_speed_sp(-900).run_timed(); 93 | rmotor.set_time_sp(500).set_speed_sp(-900).run_timed(); 94 | 95 | // Wait 0.5 seconds while motors are rolling 96 | std::this_thread::sleep_for(std::chrono::milliseconds(500)); 97 | 98 | ev3::led::all_off(); 99 | } 100 | 101 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /demos/remote_control-test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * test program for the ev3dev C++ remote_control class 3 | * 4 | * Copyright (c) 2014 - Franz Detro 5 | * 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | */ 21 | 22 | #include "ev3dev.h" 23 | 24 | #include 25 | 26 | 27 | using namespace std; 28 | using namespace ev3dev; 29 | 30 | void print_button(const char *name, bool state) 31 | { 32 | cout << name << " " << (state ? "pressed" : "released") << endl; 33 | } 34 | 35 | int main() 36 | { 37 | remote_control r; 38 | 39 | if (!r.connected()) 40 | { 41 | cout << "no infrared sensor found!" << endl; 42 | return 1; 43 | } 44 | 45 | r.on_red_down = bind(print_button, "red down", placeholders::_1); 46 | r.on_red_up = bind(print_button, "red up", placeholders::_1); 47 | r.on_blue_down = bind(print_button, "blue down", placeholders::_1); 48 | r.on_blue_up = bind(print_button, "blue up", placeholders::_1); 49 | 50 | bool stop = false; 51 | r.on_beacon = [&] (bool state) { if (state) stop = true; }; 52 | 53 | cout << "ensure that channel 1 is selected," << endl 54 | << "press middle (beacon) button to exit!" << endl << endl; 55 | 56 | while (!stop) 57 | r.process(); 58 | 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /demos/sound-test.cpp: -------------------------------------------------------------------------------- 1 | #include "ev3dev.h" 2 | 3 | using namespace ev3dev; 4 | 5 | int main() { 6 | sound::speak("Hello, I am Robot!", true); 7 | sound::tone({ 8 | {392, 350, 100}, {392, 350, 100}, {392, 350, 100}, {311.1, 250, 100}, 9 | {466.2, 25, 100}, {392, 350, 100}, {311.1, 250, 100}, {466.2, 25, 100}, 10 | {392, 700, 100}, {587.32, 350, 100}, {587.32, 350, 100}, 11 | {587.32, 350, 100}, {622.26, 250, 100}, {466.2, 25, 100}, 12 | {369.99, 350, 100}, {311.1, 250, 100}, {466.2, 25, 100}, {392, 700, 100}, 13 | {784, 350, 100}, {392, 250, 100}, {392, 25, 100}, {784, 350, 100}, 14 | {739.98, 250, 100}, {698.46, 25, 100}, {659.26, 25, 100}, 15 | {622.26, 25, 100}, {659.26, 50, 400}, {415.3, 25, 200}, {554.36, 350, 100}, 16 | {523.25, 250, 100}, {493.88, 25, 100}, {466.16, 25, 100}, {440, 25, 100}, 17 | {466.16, 50, 400}, {311.13, 25, 200}, {369.99, 350, 100}, 18 | {311.13, 250, 100}, {392, 25, 100}, {466.16, 350, 100}, {392, 250, 100}, 19 | {466.16, 25, 100}, {587.32, 700, 100}, {784, 350, 100}, {392, 250, 100}, 20 | {392, 25, 100}, {784, 350, 100}, {739.98, 250, 100}, {698.46, 25, 100}, 21 | {659.26, 25, 100}, {622.26, 25, 100}, {659.26, 50, 400}, {415.3, 25, 200}, 22 | {554.36, 350, 100}, {523.25, 250, 100}, {493.88, 25, 100}, 23 | {466.16, 25, 100}, {440, 25, 100}, {466.16, 50, 400}, {311.13, 25, 200}, 24 | {392, 350, 100}, {311.13, 250, 100}, {466.16, 25, 100}, 25 | {392.00, 300, 150}, {311.13, 250, 100}, {466.16, 25, 100}, {392, 700} 26 | }, 27 | true 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /ev3dev.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * C++ API to the sensors, motors, buttons, LEDs and battery of the ev3dev 3 | * Linux kernel for the LEGO Mindstorms EV3 hardware 4 | * 5 | * Copyright (c) 2014 - Franz Detro 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | * 21 | * Modification: 22 | * Add new button for ev3dev Release 02.00.00 (ev3dev-jessie-2014-07-12) - Christophe Chaudelet 23 | * 24 | */ 25 | 26 | #include "ev3dev.h" 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | #ifndef SYS_ROOT 51 | # define SYS_ROOT "/sys/class" 52 | #endif 53 | 54 | #ifndef FSTREAM_CACHE_SIZE 55 | # define FSTREAM_CACHE_SIZE 16 56 | #endif 57 | 58 | #ifndef NO_LINUX_HEADERS 59 | # include 60 | # include 61 | #else 62 | # define KEY_CNT 8 63 | #endif 64 | static const int bits_per_long = sizeof(long) * 8; 65 | 66 | namespace ev3dev { 67 | namespace { 68 | 69 | // This class implements a small LRU cache. It assumes the number of elements 70 | // is small, and so uses a simple linear search. 71 | template 72 | class lru_cache { 73 | private: 74 | // typedef st::pair item; 75 | // std::pair seems to be missing necessary move constructors :( 76 | struct item { 77 | K first; 78 | V second; 79 | 80 | item(const K &k) : first(k) {} 81 | item(item &&m) : first(std::move(m.first)), second(std::move(m.second)) {} 82 | }; 83 | 84 | public: 85 | lru_cache(size_t size = 3) : _size(size) {} 86 | 87 | V &operator[] (const K &k) { 88 | iterator i = find(k); 89 | if (i != _items.end()) { 90 | // Found the key, bring the item to the front. 91 | _items.splice(_items.begin(), _items, i); 92 | } else { 93 | // If the cache is full, remove oldest items to make room. 94 | while (_items.size() + 1 > _size) { 95 | _items.pop_back(); 96 | } 97 | // Insert a new default constructed value for this new key. 98 | _items.emplace_front(k); 99 | } 100 | // The new item is the most recently used. 101 | return _items.front().second; 102 | } 103 | 104 | void clear() { 105 | _items.clear(); 106 | } 107 | 108 | private: 109 | typedef typename std::list::iterator iterator; 110 | 111 | iterator find(const K &k) { 112 | return std::find_if(_items.begin(), _items.end(), 113 | [&](const item &i) { return i.first == k; }); 114 | } 115 | 116 | size_t _size; 117 | std::list _items; 118 | }; 119 | 120 | // A global cache of files. 121 | std::ifstream& ifstream_cache(const std::string &path) { 122 | static lru_cache cache(FSTREAM_CACHE_SIZE); 123 | static std::mutex mx; 124 | 125 | std::lock_guard lock(mx); 126 | return cache[path]; 127 | } 128 | 129 | std::ofstream& ofstream_cache(const std::string &path) { 130 | static lru_cache cache(FSTREAM_CACHE_SIZE); 131 | static std::mutex mx; 132 | 133 | std::lock_guard lock(mx); 134 | return cache[path]; 135 | } 136 | 137 | //----------------------------------------------------------------------------- 138 | std::ofstream &ofstream_open(const std::string &path) { 139 | std::ofstream &file = ofstream_cache(path); 140 | if (!file.is_open()) { 141 | // Don't buffer writes to avoid latency. Also saves a bit of memory. 142 | file.rdbuf()->pubsetbuf(NULL, 0); 143 | file.open(path); 144 | } else { 145 | // Clear the error bits in case something happened. 146 | file.clear(); 147 | } 148 | return file; 149 | } 150 | 151 | std::ifstream &ifstream_open(const std::string &path) { 152 | std::ifstream &file = ifstream_cache(path); 153 | if (!file.is_open()) { 154 | file.open(path); 155 | } else { 156 | // Clear the flags bits in case something happened (like reaching EOF). 157 | file.clear(); 158 | file.seekg(0, std::ios::beg); 159 | } 160 | return file; 161 | } 162 | 163 | } // namespace 164 | 165 | //----------------------------------------------------------------------------- 166 | bool device::connect( 167 | const std::string &dir, 168 | const std::string &pattern, 169 | const std::map> &match 170 | ) noexcept 171 | { 172 | using namespace std; 173 | 174 | const size_t pattern_length = pattern.length(); 175 | 176 | struct dirent *dp; 177 | DIR *dfd; 178 | 179 | if ((dfd = opendir(dir.c_str())) != nullptr) { 180 | while ((dp = readdir(dfd)) != nullptr) { 181 | if (strncmp(dp->d_name, pattern.c_str(), pattern_length)==0) { 182 | try { 183 | _path = dir + dp->d_name + '/'; 184 | 185 | bool bMatch = true; 186 | for (auto &m : match) { 187 | const auto &attribute = m.first; 188 | const auto &matches = m.second; 189 | const auto strValue = get_attr_string(attribute); 190 | 191 | if (!matches.empty() && !matches.begin()->empty() && 192 | (matches.find(strValue) == matches.end())) 193 | { 194 | bMatch = false; 195 | break; 196 | } 197 | } 198 | 199 | if (bMatch) { 200 | closedir(dfd); 201 | return true; 202 | } 203 | } catch (...) { } 204 | 205 | _path.clear(); 206 | } 207 | } 208 | 209 | closedir(dfd); 210 | } 211 | 212 | return false; 213 | } 214 | 215 | //----------------------------------------------------------------------------- 216 | int device::device_index() const { 217 | using namespace std; 218 | 219 | if (_path.empty()) 220 | throw system_error(make_error_code(errc::function_not_supported), "no device connected"); 221 | 222 | if (_device_index < 0) { 223 | unsigned f = 1; 224 | _device_index = 0; 225 | for (auto it=_path.rbegin(); it!=_path.rend(); ++it) { 226 | if(*it =='/') 227 | continue; 228 | if ((*it < '0') || (*it > '9')) 229 | break; 230 | 231 | _device_index += (*it -'0') * f; 232 | f *= 10; 233 | } 234 | } 235 | 236 | return _device_index; 237 | } 238 | 239 | //----------------------------------------------------------------------------- 240 | int device::get_attr_int(const std::string &name) const { 241 | using namespace std; 242 | 243 | if (_path.empty()) 244 | throw system_error(make_error_code(errc::function_not_supported), "no device connected"); 245 | 246 | for(int attempt = 0; attempt < 2; ++attempt) { 247 | ifstream &is = ifstream_open(_path + name); 248 | if (is.is_open()) { 249 | int result = 0; 250 | try { 251 | is >> result; 252 | return result; 253 | } catch(...) { 254 | // This could mean the sysfs attribute was recreated and the 255 | // corresponding file handle got stale. Lets close the file and try 256 | // again (once): 257 | if (attempt != 0) throw; 258 | 259 | is.close(); 260 | is.clear(); 261 | } 262 | } else break; 263 | } 264 | throw system_error(make_error_code(errc::no_such_device), _path+name); 265 | } 266 | 267 | //----------------------------------------------------------------------------- 268 | void device::set_attr_int(const std::string &name, int value) { 269 | using namespace std; 270 | 271 | if (_path.empty()) 272 | throw system_error(make_error_code(errc::function_not_supported), "no device connected"); 273 | 274 | for(int attempt = 0; attempt < 2; ++attempt) { 275 | ofstream &os = ofstream_open(_path + name); 276 | if (os.is_open()) { 277 | if (os << value) return; 278 | 279 | // An error could mean that sysfs attribute was recreated and the cached 280 | // file handle is stale. Lets close the file and try again (once): 281 | if (attempt == 0 && errno == ENODEV) { 282 | os.close(); 283 | os.clear(); 284 | } else { 285 | throw system_error(std::error_code(errno, std::system_category())); 286 | } 287 | } else { 288 | throw system_error(make_error_code(errc::no_such_device), _path + name); 289 | } 290 | } 291 | } 292 | 293 | //----------------------------------------------------------------------------- 294 | std::string device::get_attr_string(const std::string &name) const { 295 | using namespace std; 296 | 297 | if (_path.empty()) 298 | throw system_error(make_error_code(errc::function_not_supported), "no device connected"); 299 | 300 | ifstream &is = ifstream_open(_path + name); 301 | if (is.is_open()) { 302 | string result; 303 | is >> result; 304 | return result; 305 | } 306 | 307 | throw system_error(make_error_code(errc::no_such_device), _path+name); 308 | } 309 | 310 | //----------------------------------------------------------------------------- 311 | void device::set_attr_string(const std::string &name, const std::string &value) { 312 | using namespace std; 313 | 314 | if (_path.empty()) 315 | throw system_error(make_error_code(errc::function_not_supported), "no device connected"); 316 | 317 | ofstream &os = ofstream_open(_path + name); 318 | if (os.is_open()) { 319 | if (!(os << value)) throw system_error(std::error_code(errno, std::system_category())); 320 | return; 321 | } 322 | 323 | throw system_error(make_error_code(errc::no_such_device), _path+name); 324 | } 325 | 326 | //----------------------------------------------------------------------------- 327 | std::string device::get_attr_line(const std::string &name) const { 328 | using namespace std; 329 | 330 | if (_path.empty()) 331 | throw system_error(make_error_code(errc::function_not_supported), "no device connected"); 332 | 333 | ifstream &is = ifstream_open(_path + name); 334 | if (is.is_open()) { 335 | string result; 336 | getline(is, result); 337 | return result; 338 | } 339 | 340 | throw system_error(make_error_code(errc::no_such_device), _path+name); 341 | } 342 | 343 | //----------------------------------------------------------------------------- 344 | mode_set device::get_attr_set( 345 | const std::string &name, std::string *pCur) const 346 | { 347 | using namespace std; 348 | 349 | string s = get_attr_line(name); 350 | 351 | mode_set result; 352 | size_t pos, last_pos = 0; 353 | string t; 354 | do { 355 | pos = s.find(' ', last_pos); 356 | 357 | if (pos != string::npos) { 358 | t = s.substr(last_pos, pos-last_pos); 359 | last_pos = pos+1; 360 | } else { 361 | t = s.substr(last_pos); 362 | } 363 | 364 | if (!t.empty()) { 365 | if (*t.begin()=='[') { 366 | t = t.substr(1, t.length()-2); 367 | if (pCur) 368 | *pCur = t; 369 | } 370 | result.insert(t); 371 | } 372 | } while (pos!=string::npos); 373 | 374 | return result; 375 | } 376 | 377 | //----------------------------------------------------------------------------- 378 | std::string device::get_attr_from_set(const std::string &name) const { 379 | using namespace std; 380 | 381 | string s = get_attr_line(name); 382 | 383 | size_t pos, last_pos = 0; 384 | string t; 385 | do { 386 | pos = s.find(' ', last_pos); 387 | 388 | if (pos != string::npos) { 389 | t = s.substr(last_pos, pos-last_pos); 390 | last_pos = pos+1; 391 | } else { 392 | t = s.substr(last_pos); 393 | } 394 | 395 | if (!t.empty()) { 396 | if (*t.begin()=='[') { 397 | return t.substr(1, t.length()-2); 398 | } 399 | } 400 | } while (pos!=string::npos); 401 | 402 | return { "none" }; 403 | } 404 | 405 | //----------------------------------------------------------------------------- 406 | constexpr char sensor::ev3_touch[]; 407 | constexpr char sensor::ev3_color[]; 408 | constexpr char sensor::ev3_ultrasonic[]; 409 | constexpr char sensor::ev3_gyro[]; 410 | constexpr char sensor::ev3_infrared[]; 411 | constexpr char sensor::nxt_touch[]; 412 | constexpr char sensor::nxt_light[]; 413 | constexpr char sensor::nxt_sound[]; 414 | constexpr char sensor::nxt_ultrasonic[]; 415 | constexpr char sensor::nxt_i2c_sensor[]; 416 | constexpr char sensor::nxt_analog[]; 417 | 418 | //----------------------------------------------------------------------------- 419 | sensor::sensor(address_type address) { 420 | connect({{ "address", { address }}}); 421 | } 422 | 423 | //----------------------------------------------------------------------------- 424 | sensor::sensor(address_type address, const std::set &types) { 425 | connect({{ "address", { address }}, { "driver_name", types }}); 426 | } 427 | 428 | //----------------------------------------------------------------------------- 429 | bool sensor::connect(const std::map> &match) noexcept 430 | { 431 | static const std::string _strClassDir { SYS_ROOT "/lego-sensor/" }; 432 | static const std::string _strPattern { "sensor" }; 433 | 434 | try { 435 | if (device::connect(_strClassDir, _strPattern, match)) { 436 | return true; 437 | } 438 | } catch (...) { } 439 | 440 | _path.clear(); 441 | 442 | return false; 443 | } 444 | 445 | //----------------------------------------------------------------------------- 446 | std::string sensor::type_name() const { 447 | auto type = driver_name(); 448 | if (type.empty()) { 449 | static const std::string s(""); 450 | return s; 451 | } 452 | 453 | static const std::map lookup_table { 454 | { ev3_touch, "EV3 touch" }, 455 | { ev3_color, "EV3 color" }, 456 | { ev3_ultrasonic, "EV3 ultrasonic" }, 457 | { ev3_gyro, "EV3 gyro" }, 458 | { ev3_infrared, "EV3 infrared" }, 459 | { nxt_touch, "NXT touch" }, 460 | { nxt_light, "NXT light" }, 461 | { nxt_sound, "NXT sound" }, 462 | { nxt_ultrasonic, "NXT ultrasonic" }, 463 | { nxt_i2c_sensor, "I2C sensor" } 464 | }; 465 | 466 | auto s = lookup_table.find(type); 467 | if (s != lookup_table.end()) 468 | return s->second; 469 | 470 | return type; 471 | } 472 | 473 | //----------------------------------------------------------------------------- 474 | int sensor::value(unsigned index) const { 475 | if (static_cast(index) >= num_values()) 476 | throw std::invalid_argument("index"); 477 | 478 | char svalue[7] = "value0"; 479 | svalue[5] += index; 480 | 481 | return get_attr_int(svalue); 482 | } 483 | 484 | //----------------------------------------------------------------------------- 485 | float sensor::float_value(unsigned index) const { 486 | return value(index) * powf(10, -decimals()); 487 | } 488 | 489 | //----------------------------------------------------------------------------- 490 | const std::vector& sensor::bin_data() const { 491 | using namespace std; 492 | 493 | if (_path.empty()) 494 | throw system_error(make_error_code(errc::function_not_supported), "no device connected"); 495 | 496 | if (_bin_data.empty()) { 497 | static const map lookup_table { 498 | {"u8", 1}, 499 | {"s8", 1}, 500 | {"u16", 2}, 501 | {"s16", 2}, 502 | {"s16_be", 2}, 503 | {"s32", 4}, 504 | {"float", 4} 505 | }; 506 | 507 | int value_size = 1; 508 | 509 | auto s = lookup_table.find(bin_data_format()); 510 | if (s != lookup_table.end()) 511 | value_size = s->second; 512 | 513 | _bin_data.resize(num_values() * value_size); 514 | } 515 | 516 | const string fname = _path + "bin_data"; 517 | ifstream &is = ifstream_open(fname); 518 | if (is.is_open()) { 519 | is.read(_bin_data.data(), _bin_data.size()); 520 | return _bin_data; 521 | } 522 | 523 | throw system_error(make_error_code(errc::no_such_device), fname); 524 | } 525 | 526 | //----------------------------------------------------------------------------- 527 | i2c_sensor::i2c_sensor(address_type address, const std::set &types) 528 | : sensor(address, types) 529 | { } 530 | 531 | //----------------------------------------------------------------------------- 532 | constexpr char touch_sensor::mode_touch[]; 533 | 534 | touch_sensor::touch_sensor(address_type address) 535 | : sensor(address, { ev3_touch, nxt_touch }) 536 | { } 537 | 538 | //----------------------------------------------------------------------------- 539 | constexpr char color_sensor::mode_col_reflect[]; 540 | constexpr char color_sensor::mode_col_ambient[]; 541 | constexpr char color_sensor::mode_col_color[]; 542 | constexpr char color_sensor::mode_ref_raw[]; 543 | constexpr char color_sensor::mode_rgb_raw[]; 544 | constexpr char color_sensor::color_nocolor[]; 545 | constexpr char color_sensor::color_black[]; 546 | constexpr char color_sensor::color_blue[]; 547 | constexpr char color_sensor::color_green[]; 548 | constexpr char color_sensor::color_yellow[]; 549 | constexpr char color_sensor::color_red[]; 550 | constexpr char color_sensor::color_white[]; 551 | constexpr char color_sensor::color_brown[]; 552 | 553 | color_sensor::color_sensor(address_type address) 554 | : sensor(address, { ev3_color }) 555 | { } 556 | 557 | //----------------------------------------------------------------------------- 558 | constexpr char ultrasonic_sensor::mode_us_dist_cm[]; 559 | constexpr char ultrasonic_sensor::mode_us_dist_in[]; 560 | constexpr char ultrasonic_sensor::mode_us_listen[]; 561 | constexpr char ultrasonic_sensor::mode_us_si_cm[]; 562 | constexpr char ultrasonic_sensor::mode_us_si_in[]; 563 | 564 | ultrasonic_sensor::ultrasonic_sensor(address_type address) 565 | : sensor(address, { ev3_ultrasonic, nxt_ultrasonic }) 566 | { } 567 | 568 | ultrasonic_sensor::ultrasonic_sensor(address_type address, const std::set& sensorTypes) 569 | : sensor(address, sensorTypes) 570 | { } 571 | 572 | //----------------------------------------------------------------------------- 573 | char gyro_sensor::mode_gyro_ang[] = "GYRO-ANG"; 574 | char gyro_sensor::mode_gyro_rate[] = "GYRO-RATE"; 575 | char gyro_sensor::mode_gyro_fas[] = "GYRO-FAS"; 576 | char gyro_sensor::mode_gyro_g_a[] = "GYRO-G&A"; 577 | char gyro_sensor::mode_gyro_cal[] = "GYRO-CAL"; 578 | char gyro_sensor::mode_tilt_rate[] = "TILT-RATE"; 579 | char gyro_sensor::mode_tilt_ang[] = "TILT-ANG"; 580 | 581 | gyro_sensor::gyro_sensor(address_type address) 582 | : sensor(address, { ev3_gyro }) 583 | { } 584 | 585 | //----------------------------------------------------------------------------- 586 | char infrared_sensor::mode_ir_prox[] = "IR-PROX"; 587 | char infrared_sensor::mode_ir_seek[] = "IR-SEEK"; 588 | char infrared_sensor::mode_ir_remote[] = "IR-REMOTE"; 589 | char infrared_sensor::mode_ir_rem_a[] = "IR-REM-A"; 590 | char infrared_sensor::mode_ir_cal[] = "IR-CAL"; 591 | 592 | infrared_sensor::infrared_sensor(address_type address) 593 | : sensor(address, { ev3_infrared }) 594 | { } 595 | 596 | //----------------------------------------------------------------------------- 597 | char sound_sensor::mode_db[] = "DB"; 598 | char sound_sensor::mode_dba[] = "DBA"; 599 | 600 | sound_sensor::sound_sensor(address_type address) 601 | : sensor(address, { nxt_sound, nxt_analog }) 602 | { 603 | if (connected() && driver_name() == nxt_analog) { 604 | lego_port port(address); 605 | 606 | if (port.connected()) { 607 | port.set_set_device(nxt_sound); 608 | 609 | if (port.status() != nxt_sound) { 610 | // Failed to load lego-nxt-sound friver. Wrong port? 611 | _path.clear(); 612 | } 613 | } else { 614 | _path.clear(); 615 | } 616 | } 617 | } 618 | 619 | //----------------------------------------------------------------------------- 620 | char light_sensor::mode_reflect[] = "REFLECT"; 621 | char light_sensor::mode_ambient[] = "AMBIENT"; 622 | 623 | light_sensor::light_sensor(address_type address) 624 | : sensor(address, { nxt_light }) 625 | { } 626 | 627 | //----------------------------------------------------------------------------- 628 | char motor::motor_large[] = "lego-ev3-l-motor"; 629 | char motor::motor_medium[] = "lego-ev3-m-motor"; 630 | char motor::motor_nxt[] = "lego-nxt-motor"; 631 | char motor::command_run_forever[] = "run-forever"; 632 | char motor::command_run_to_abs_pos[] = "run-to-abs-pos"; 633 | char motor::command_run_to_rel_pos[] = "run-to-rel-pos"; 634 | char motor::command_run_timed[] = "run-timed"; 635 | char motor::command_run_direct[] = "run-direct"; 636 | char motor::command_stop[] = "stop"; 637 | char motor::command_reset[] = "reset"; 638 | char motor::encoder_polarity_normal[] = "normal"; 639 | char motor::encoder_polarity_inversed[] = "inversed"; 640 | char motor::polarity_normal[] = "normal"; 641 | char motor::polarity_inversed[] = "inversed"; 642 | char motor::state_running[] = "running"; 643 | char motor::state_ramping[] = "ramping"; 644 | char motor::state_holding[] = "holding"; 645 | char motor::state_overloaded[] = "overloaded"; 646 | char motor::state_stalled[] = "stalled"; 647 | char motor::stop_action_coast[] = "coast"; 648 | char motor::stop_action_brake[] = "brake"; 649 | char motor::stop_action_hold[] = "hold"; 650 | 651 | //----------------------------------------------------------------------------- 652 | motor::motor(address_type address) { 653 | connect({{ "address", { address } }}); 654 | } 655 | 656 | //----------------------------------------------------------------------------- 657 | motor::motor(address_type address, const motor_type &t) { 658 | connect({{ "address", { address } }, { "driver_name", { t }}}); 659 | } 660 | 661 | //----------------------------------------------------------------------------- 662 | bool motor::connect(const std::map> &match) noexcept 663 | { 664 | static const std::string _strClassDir { SYS_ROOT "/tacho-motor/" }; 665 | static const std::string _strPattern { "motor" }; 666 | 667 | try { 668 | return device::connect(_strClassDir, _strPattern, match); 669 | } catch (...) { } 670 | 671 | _path.clear(); 672 | 673 | return false; 674 | } 675 | 676 | //----------------------------------------------------------------------------- 677 | medium_motor::medium_motor(address_type address) 678 | : motor(address, motor_medium) 679 | { } 680 | 681 | //----------------------------------------------------------------------------- 682 | large_motor::large_motor(address_type address) 683 | : motor(address, motor_large) 684 | { } 685 | 686 | //----------------------------------------------------------------------------- 687 | nxt_motor::nxt_motor(address_type address) 688 | : motor(address, motor_nxt) 689 | { } 690 | 691 | //----------------------------------------------------------------------------- 692 | dc_motor::dc_motor(address_type address) { 693 | static const std::string _strClassDir { SYS_ROOT "/dc-motor/" }; 694 | static const std::string _strPattern { "motor" }; 695 | 696 | connect(_strClassDir, _strPattern, {{ "address", { address }}}); 697 | } 698 | 699 | char dc_motor::command_run_forever[] = "run-forever"; 700 | char dc_motor::command_run_timed[] = "run-timed"; 701 | char dc_motor::command_run_direct[] = "run-direct"; 702 | char dc_motor::command_stop[] = "stop"; 703 | char dc_motor::polarity_normal[] = "normal"; 704 | char dc_motor::polarity_inversed[] = "inversed"; 705 | char dc_motor::stop_action_coast[] = "coast"; 706 | char dc_motor::stop_action_brake[] = "brake"; 707 | 708 | //----------------------------------------------------------------------------- 709 | servo_motor::servo_motor(address_type address) { 710 | static const std::string _strClassDir { SYS_ROOT "/servo-motor/" }; 711 | static const std::string _strPattern { "motor" }; 712 | 713 | connect(_strClassDir, _strPattern, {{ "address", { address }}}); 714 | } 715 | 716 | char servo_motor::command_run[] = "run"; 717 | char servo_motor::command_float[] = "float"; 718 | char servo_motor::polarity_normal[] = "normal"; 719 | char servo_motor::polarity_inversed[] = "inversed"; 720 | 721 | //----------------------------------------------------------------------------- 722 | led::led(std::string name) { 723 | static const std::string _strClassDir { SYS_ROOT "/leds/" }; 724 | connect(_strClassDir, name, std::map>()); 725 | } 726 | 727 | //----------------------------------------------------------------------------- 728 | void led::flash(unsigned on_ms, unsigned off_ms) { 729 | static const mode_type timer("timer"); 730 | set_trigger(timer); 731 | if (on_ms) { 732 | // A workaround for ev3dev/ev3dev#225. 733 | // It takes some time for delay_{on,off} sysfs attributes to appear after 734 | // led trigger has been set to "timer". 735 | for (int i = 0; ; ++i) { 736 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 737 | try { 738 | set_delay_on (on_ms ); 739 | set_delay_off(off_ms); 740 | break; 741 | } catch(...) { 742 | if (i >= 5) throw; 743 | } 744 | } 745 | } 746 | } 747 | 748 | //----------------------------------------------------------------------------- 749 | #if defined(EV3DEV_PLATFORM_BRICKPI) 750 | 751 | led led::blue_led1{"brickpi:led1:blue:ev3dev"}; 752 | led led::blue_led2{"brickpi:led2:blue:ev3dev"}; 753 | 754 | std::vector led::led1{ &led::blue_led1 }; 755 | std::vector led::led2{ &led::blue_led2 }; 756 | 757 | std::vector led::black{ static_cast(0) }; 758 | std::vector led::blue{ static_cast(1) }; 759 | 760 | //----------------------------------------------------------------------------- 761 | void led::all_off() { 762 | 763 | blue_led1.off(); 764 | blue_led2.off(); 765 | 766 | } 767 | 768 | #elif defined(EV3DEV_PLATFORM_BRICKPI3) 769 | led led::amber_led1{"brickpi3:amber:ev3dev"}; 770 | 771 | std::vector led::led1{ &led::amber_led1 }; 772 | 773 | std::vector led::black{ static_cast(0) }; 774 | std::vector led::blue{ static_cast(1) }; 775 | 776 | //----------------------------------------------------------------------------- 777 | void led::all_off() { 778 | 779 | amber_led1.off(); 780 | 781 | } 782 | #elif defined(EV3DEV_PLATFORM_PISTORMS) 783 | 784 | led led::red_left{"pistorms:BB:red:ev3dev"}; 785 | led led::red_right{"pistorms:BA:red:ev3dev"}; 786 | led led::green_left{"pistorms:BB:green:ev3dev"}; 787 | led led::green_right{"pistorms:BA:green:ev3dev"}; 788 | led led::blue_left{"pistorms:BB:blue:ev3dev"}; 789 | led led::blue_right{"pistorms:BA:blue:ev3dev"}; 790 | 791 | std::vector led::left{ &led::red_left, &led::green_left, &led::blue_left }; 792 | std::vector led::right{ &led::red_right, &led::green_right, &led::blue_right }; 793 | 794 | std::vector led::black{ static_cast(0), static_cast(0), static_cast(0) }; 795 | std::vector led::red{ static_cast(1), static_cast(0), static_cast(0) }; 796 | std::vector led::green{ static_cast(0), static_cast(1), static_cast(0) }; 797 | std::vector led::blue{ static_cast(0), static_cast(0), static_cast(1) }; 798 | std::vector led::yellow{ static_cast(1), static_cast(1), static_cast(0) }; 799 | std::vector led::purple{ static_cast(1), static_cast(0), static_cast(1) }; 800 | std::vector led::cyan{ static_cast(0), static_cast(1), static_cast(1) }; 801 | std::vector led::white{ static_cast(1), static_cast(1), static_cast(1) }; 802 | std::vector led::orange{ static_cast(1), static_cast(0.5), static_cast(0) }; 803 | 804 | //----------------------------------------------------------------------------- 805 | void led::all_off() { 806 | 807 | red_left.off(); 808 | red_right.off(); 809 | green_left.off(); 810 | green_right.off(); 811 | blue_left.off(); 812 | blue_right.off(); 813 | 814 | } 815 | 816 | #else 817 | 818 | led led::red_left{"led0:red:brick-status"}; 819 | led led::red_right{"led1:red:brick-status"}; 820 | led led::green_left{"led0:green:brick-status"}; 821 | led led::green_right{"led1:green:brick-status"}; 822 | 823 | std::vector led::left{ &led::red_left, &led::green_left }; 824 | std::vector led::right{ &led::red_right, &led::green_right }; 825 | 826 | std::vector led::black{ static_cast(0), static_cast(0) }; 827 | std::vector led::red{ static_cast(1), static_cast(0) }; 828 | std::vector led::green{ static_cast(0), static_cast(1) }; 829 | std::vector led::amber{ static_cast(1), static_cast(1) }; 830 | std::vector led::orange{ static_cast(1), static_cast(0.5) }; 831 | std::vector led::yellow{ static_cast(0.1), static_cast(1) }; 832 | 833 | //----------------------------------------------------------------------------- 834 | void led::all_off() { 835 | 836 | red_left.off(); 837 | red_right.off(); 838 | green_left.off(); 839 | green_right.off(); 840 | 841 | } 842 | 843 | #endif 844 | 845 | //----------------------------------------------------------------------------- 846 | void led::set_color(const std::vector &group, const std::vector &color) { 847 | const size_t n = std::min(group.size(), color.size()); 848 | for(size_t i = 0; i < n; ++i) 849 | group[i]->set_brightness_pct(color[i]); 850 | } 851 | 852 | //----------------------------------------------------------------------------- 853 | power_supply power_supply::battery { "" }; 854 | 855 | //----------------------------------------------------------------------------- 856 | power_supply::power_supply(std::string name) { 857 | static const std::string _strClassDir { SYS_ROOT "/power_supply/" }; 858 | 859 | if (name.empty()) 860 | name = "lego-ev3-battery"; 861 | 862 | connect(_strClassDir, name, std::map>()); 863 | } 864 | 865 | //----------------------------------------------------------------------------- 866 | button::file_descriptor::file_descriptor(const char *path, int flags) 867 | : _fd(open(path, flags)) 868 | { } 869 | 870 | button::file_descriptor::~file_descriptor() { 871 | if (_fd != -1) close(_fd); 872 | } 873 | 874 | //----------------------------------------------------------------------------- 875 | button::button(int bit) 876 | : _bit(bit), 877 | _buf((KEY_CNT + bits_per_long - 1) / bits_per_long), 878 | _fd( new file_descriptor("/dev/input/by-path/platform-gpio_keys-event", O_RDONLY) ) 879 | { } 880 | 881 | //----------------------------------------------------------------------------- 882 | bool button::pressed() const { 883 | #ifndef NO_LINUX_HEADERS 884 | if (ioctl(*_fd, EVIOCGKEY(_buf.size()), _buf.data()) < 0) { 885 | // handle error 886 | } 887 | #endif 888 | return (_buf[_bit / bits_per_long] & 1 << (_bit % bits_per_long)); 889 | } 890 | 891 | //----------------------------------------------------------------------------- 892 | bool button::process() { 893 | bool new_state = pressed(); 894 | 895 | if (new_state != _state) { 896 | _state = new_state; 897 | if (onclick) onclick(new_state); 898 | return true; 899 | } 900 | 901 | return false; 902 | } 903 | 904 | //----------------------------------------------------------------------------- 905 | #ifndef NO_LINUX_HEADERS 906 | button button::back (KEY_BACKSPACE); 907 | button button::left (KEY_LEFT); 908 | button button::right(KEY_RIGHT); 909 | button button::up (KEY_UP); 910 | button button::down (KEY_DOWN); 911 | button button::enter(KEY_ENTER); 912 | #endif 913 | 914 | //----------------------------------------------------------------------------- 915 | bool button::process_all() { 916 | std::array changed{{ 917 | back. process(), 918 | left. process(), 919 | right.process(), 920 | up. process(), 921 | down. process(), 922 | enter.process() 923 | }}; 924 | return std::any_of(changed.begin(), changed.end(), [](bool c){ return c; }); 925 | } 926 | 927 | //----------------------------------------------------------------------------- 928 | void sound::beep(const std::string &args, bool bSynchronous) { 929 | std::ostringstream cmd; 930 | cmd << "/usr/bin/beep " << args; 931 | if (!bSynchronous) cmd << " &"; 932 | std::system(cmd.str().c_str()); 933 | } 934 | 935 | //----------------------------------------------------------------------------- 936 | void sound::tone( 937 | const std::vector< std::vector > &sequence, 938 | bool bSynchronous 939 | ) 940 | { 941 | std::ostringstream args; 942 | bool first = true; 943 | 944 | for(auto v : sequence) { 945 | if (first) { 946 | first = false; 947 | } else { 948 | args << " -n"; 949 | } 950 | 951 | if (v.size() > 0) { 952 | args << " -f " << v[0]; 953 | } else { 954 | continue; 955 | } 956 | 957 | if (v.size() > 1) { 958 | args << " -l " << v[1]; 959 | } else { 960 | continue; 961 | } 962 | 963 | if (v.size() > 2) { 964 | args << " -D " << v[2]; 965 | } else { 966 | continue; 967 | } 968 | } 969 | 970 | beep(args.str(), bSynchronous); 971 | } 972 | 973 | //----------------------------------------------------------------------------- 974 | void sound::tone(float frequency, float ms, bool bSynchronous) { 975 | tone({{frequency, ms, 0.0f}}, bSynchronous); 976 | } 977 | 978 | //----------------------------------------------------------------------------- 979 | void sound::play(const std::string &soundfile, bool bSynchronous) { 980 | std::ostringstream cmd; 981 | cmd << "/usr/bin/aplay -q " << soundfile; 982 | 983 | if (!bSynchronous) cmd << " &"; 984 | 985 | std::system(cmd.str().c_str()); 986 | } 987 | 988 | //----------------------------------------------------------------------------- 989 | void sound::speak(const std::string &text, bool bSynchronous) { 990 | std::ostringstream cmd; 991 | 992 | cmd << "/usr/bin/espeak -a 200 --stdout \"" << text << "\"" 993 | << " | /usr/bin/aplay -q"; 994 | 995 | if (!bSynchronous) cmd << " &"; 996 | 997 | std::system(cmd.str().c_str()); 998 | } 999 | 1000 | //----------------------------------------------------------------------------- 1001 | lcd::lcd() : 1002 | _fb(nullptr), _fbsize(0), _llength(0), _xres(0), _yres(0), _bpp(0) 1003 | { 1004 | init(); 1005 | } 1006 | 1007 | //----------------------------------------------------------------------------- 1008 | lcd::~lcd() { 1009 | deinit(); 1010 | } 1011 | 1012 | //----------------------------------------------------------------------------- 1013 | void lcd::fill(unsigned char pixel) { 1014 | if (_fb && _fbsize) { 1015 | memset(_fb, pixel, _fbsize); 1016 | } 1017 | } 1018 | 1019 | //----------------------------------------------------------------------------- 1020 | void lcd::init() { 1021 | using namespace std; 1022 | 1023 | #ifdef _LINUX_FB_H 1024 | int fbf = open("/dev/fb0", O_RDWR); 1025 | if (fbf < 0) 1026 | return; 1027 | 1028 | fb_fix_screeninfo i; 1029 | if (ioctl(fbf, FBIOGET_FSCREENINFO, &i) < 0) 1030 | return; 1031 | 1032 | _fbsize = i.smem_len; 1033 | _llength = i.line_length; 1034 | 1035 | _fb = (unsigned char*)mmap(NULL, _fbsize, PROT_READ|PROT_WRITE, MAP_SHARED, fbf, 0); 1036 | if (_fb == nullptr) 1037 | return; 1038 | 1039 | fb_var_screeninfo v; 1040 | 1041 | if (ioctl(fbf, FBIOGET_VSCREENINFO, &v) < 0) 1042 | return; 1043 | 1044 | _xres = v.xres; 1045 | _yres = v.yres; 1046 | _bpp = v.bits_per_pixel; 1047 | #endif 1048 | } 1049 | 1050 | //----------------------------------------------------------------------------- 1051 | void lcd::deinit() { 1052 | if (_fb) { 1053 | munmap(_fb, 0); 1054 | } 1055 | 1056 | _fbsize = 0; 1057 | } 1058 | 1059 | //----------------------------------------------------------------------------- 1060 | remote_control::remote_control(unsigned channel) 1061 | : _sensor(new infrared_sensor), _owns_sensor(true) 1062 | { 1063 | if ((channel >= 1) && (channel <=4)) 1064 | _channel = channel-1; 1065 | 1066 | if (_sensor->connected()) 1067 | _sensor->set_mode(infrared_sensor::mode_ir_remote); 1068 | } 1069 | 1070 | //----------------------------------------------------------------------------- 1071 | remote_control::remote_control(infrared_sensor &ir, unsigned channel) 1072 | : _sensor(&ir), _owns_sensor(false) 1073 | { 1074 | if ((channel >= 1) && (channel <=4)) 1075 | _channel = channel-1; 1076 | 1077 | if (_sensor->connected()) 1078 | _sensor->set_mode(infrared_sensor::mode_ir_remote); 1079 | } 1080 | 1081 | //----------------------------------------------------------------------------- 1082 | remote_control::~remote_control() { 1083 | if (_owns_sensor) 1084 | delete _sensor; 1085 | } 1086 | 1087 | //----------------------------------------------------------------------------- 1088 | bool remote_control::process() { 1089 | int value = _sensor->value(_channel); 1090 | if (value != _value) { 1091 | on_value_changed(value); 1092 | _value = value; 1093 | return true; 1094 | } 1095 | 1096 | return false; 1097 | } 1098 | 1099 | //----------------------------------------------------------------------------- 1100 | void remote_control::on_value_changed(int value) { 1101 | int new_state = 0; 1102 | 1103 | switch (value) { 1104 | case 1: 1105 | new_state = red_up; 1106 | break; 1107 | case 2: 1108 | new_state = red_down; 1109 | break; 1110 | case 3: 1111 | new_state = blue_up; 1112 | break; 1113 | case 4: 1114 | new_state = blue_down; 1115 | break; 1116 | case 5: 1117 | new_state = red_up | blue_up; 1118 | break; 1119 | case 6: 1120 | new_state = red_up | blue_down; 1121 | break; 1122 | case 7: 1123 | new_state = red_down | blue_up; 1124 | break; 1125 | case 8: 1126 | new_state = red_down | blue_down; 1127 | break; 1128 | case 9: 1129 | new_state = beacon; 1130 | break; 1131 | case 10: 1132 | new_state = red_up | red_down; 1133 | break; 1134 | case 11: 1135 | new_state = blue_up | blue_down; 1136 | break; 1137 | } 1138 | 1139 | if (((new_state & red_up) != (_state & red_up)) && 1140 | static_cast(on_red_up)) 1141 | on_red_up(new_state & red_up); 1142 | 1143 | if (((new_state & red_down) != (_state & red_down)) && 1144 | static_cast(on_red_down)) 1145 | on_red_down(new_state & red_down); 1146 | 1147 | if (((new_state & blue_up) != (_state & blue_up)) && 1148 | static_cast(on_blue_up)) 1149 | on_blue_up(new_state & blue_up); 1150 | 1151 | if (((new_state & blue_down) != (_state & blue_down)) && 1152 | static_cast(on_blue_down)) 1153 | on_blue_down(new_state & blue_down); 1154 | 1155 | if (((new_state & beacon) != (_state & beacon)) && 1156 | static_cast(on_beacon)) 1157 | on_beacon(new_state & beacon); 1158 | 1159 | if ((new_state != _state) && 1160 | static_cast(on_state_change)) 1161 | on_state_change(new_state); 1162 | 1163 | _state = new_state; 1164 | } 1165 | 1166 | //----------------------------------------------------------------------------- 1167 | lego_port::lego_port(address_type address) { 1168 | connect({{ "address", { address } }}); 1169 | } 1170 | 1171 | //----------------------------------------------------------------------------- 1172 | bool lego_port::connect(const std::map> &match) noexcept 1173 | { 1174 | static const std::string _strClassDir { SYS_ROOT "/lego-port/" }; 1175 | static const std::string _strPattern { "port" }; 1176 | 1177 | try { 1178 | return device::connect(_strClassDir, _strPattern, match); 1179 | } catch (...) { } 1180 | 1181 | _path.clear(); 1182 | 1183 | return false; 1184 | } 1185 | 1186 | } // namespace ev3dev 1187 | -------------------------------------------------------------------------------- /ev3dev.h: -------------------------------------------------------------------------------- 1 | /* 2 | * C++ API to the sensors, motors, buttons, LEDs and battery of the ev3dev 3 | * Linux kernel for the LEGO Mindstorms EV3 hardware 4 | * 5 | * Copyright (c) 2014 - Franz Detro 6 | * 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | * 22 | * Modification: 23 | * Add new button management for ev3dev Release 02.00.00 (ev3dev-jessie-2014-07-12) - Christophe Chaudelet 24 | * 25 | */ 26 | 27 | #pragma once 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | namespace ev3dev { 39 | 40 | //----------------------------------------------------------------------------- 41 | typedef std::string device_type; 42 | typedef std::string mode_type; 43 | typedef std::set mode_set; 44 | typedef std::string address_type; 45 | 46 | //----------------------------------------------------------------------------- 47 | const address_type INPUT_AUTO; //!< Automatic input selection 48 | const address_type OUTPUT_AUTO; //!< Automatic output selection 49 | 50 | #if defined(EV3DEV_PLATFORM_BRICKPI) 51 | constexpr char INPUT_1[] = "ttyAMA0:in1"; //!< Sensor port 1 52 | constexpr char INPUT_2[] = "ttyAMA0:in2"; //!< Sensor port 2 53 | constexpr char INPUT_3[] = "ttyAMA0:in3"; //!< Sensor port 3 54 | constexpr char INPUT_4[] = "ttyAMA0:in4"; //!< Sensor port 4 55 | 56 | constexpr char OUTPUT_A[] = "ttyAMA0:outA"; //!< Motor port A 57 | constexpr char OUTPUT_B[] = "ttyAMA0:outB"; //!< Motor port B 58 | constexpr char OUTPUT_C[] = "ttyAMA0:outC"; //!< Motor port C 59 | constexpr char OUTPUT_D[] = "ttyAMA0:outD"; //!< Motor port D 60 | #elif defined(EV3DEV_PLATFORM_BRICKPI3) 61 | constexpr char INPUT_1[] = "spi0.1:S1"; //!< Sensor port 1 62 | constexpr char INPUT_2[] = "spi0.1:S2"; //!< Sensor port 2 63 | constexpr char INPUT_3[] = "spi0.1:S3"; //!< Sensor port 3 64 | constexpr char INPUT_4[] = "spi0.1:S4"; //!< Sensor port 4 65 | 66 | constexpr char OUTPUT_A[] = "spi0.1:MA"; //!< Motor port A 67 | constexpr char OUTPUT_B[] = "spi0.1:MB"; //!< Motor port B 68 | constexpr char OUTPUT_C[] = "spi0.1:MC"; //!< Motor port C 69 | constexpr char OUTPUT_D[] = "spi0.1:MD"; //!< Motor port D 70 | #elif defined(EV3DEV_PLATFORM_PISTORMS) 71 | constexpr char INPUT_1[] = "pistorms:BAS1"; //!< Sensor port 1 72 | constexpr char INPUT_2[] = "pistorms:BAS2"; //!< Sensor port 2 73 | constexpr char INPUT_3[] = "pistorms:BBS1"; //!< Sensor port 3 74 | constexpr char INPUT_4[] = "pistorms:BBS2"; //!< Sensor port 4 75 | 76 | constexpr char OUTPUT_A[] = "pistorms:BAM1"; //!< Motor port A 77 | constexpr char OUTPUT_B[] = "pistorms:BAM2"; //!< Motor port B 78 | constexpr char OUTPUT_C[] = "pistorms:BBM1"; //!< Motor port C 79 | constexpr char OUTPUT_D[] = "pistorms:BBM2"; //!< Motor port D 80 | #else // assume EV3DEV_PLATFORM_EV3 81 | constexpr char INPUT_1[] = "ev3-ports:in1"; //!< Sensor port 1 82 | constexpr char INPUT_2[] = "ev3-ports:in2"; //!< Sensor port 2 83 | constexpr char INPUT_3[] = "ev3-ports:in3"; //!< Sensor port 3 84 | constexpr char INPUT_4[] = "ev3-ports:in4"; //!< Sensor port 4 85 | 86 | constexpr char OUTPUT_A[] = "ev3-ports:outA"; //!< Motor port A 87 | constexpr char OUTPUT_B[] = "ev3-ports:outB"; //!< Motor port B 88 | constexpr char OUTPUT_C[] = "ev3-ports:outC"; //!< Motor port C 89 | constexpr char OUTPUT_D[] = "ev3-ports:outD"; //!< Motor port D 90 | #endif 91 | 92 | //----------------------------------------------------------------------------- 93 | // Generic device class. 94 | //----------------------------------------------------------------------------- 95 | class device { 96 | public: 97 | bool connect(const std::string &dir, 98 | const std::string &pattern, 99 | const std::map> &match) noexcept; 100 | 101 | inline bool connected() const { return !_path.empty(); } 102 | 103 | int device_index() const; 104 | 105 | int get_attr_int (const std::string &name) const; 106 | void set_attr_int (const std::string &name, 107 | int value); 108 | std::string get_attr_string(const std::string &name) const; 109 | void set_attr_string(const std::string &name, 110 | const std::string &value); 111 | 112 | std::string get_attr_line (const std::string &name) const; 113 | mode_set get_attr_set (const std::string &name, 114 | std::string *pCur = nullptr) const; 115 | 116 | std::string get_attr_from_set(const std::string &name) const; 117 | 118 | protected: 119 | std::string _path; 120 | mutable int _device_index = -1; 121 | }; 122 | 123 | //----------------------------------------------------------------------------- 124 | // The sensor class provides a uniform interface for using most of the 125 | // sensors available for the EV3. The various underlying device drivers will 126 | // create a `lego-sensor` device for interacting with the sensors. 127 | // 128 | // Sensors are primarily controlled by setting the `mode` and monitored by 129 | // reading the `value` attributes. Values can be converted to floating point 130 | // if needed by `value` / 10.0 ^ `decimals`. 131 | // 132 | // Since the name of the `sensor` device node does not correspond to the port 133 | // that a sensor is plugged in to, you must look at the `address` attribute if 134 | // you need to know which port a sensor is plugged in to. However, if you don't 135 | // have more than one sensor of each type, you can just look for a matching 136 | // `driver_name`. Then it will not matter which port a sensor is plugged in to - your 137 | // program will still work. 138 | //----------------------------------------------------------------------------- 139 | class sensor : protected device { 140 | public: 141 | typedef device_type sensor_type; 142 | 143 | static constexpr char ev3_touch[] = "lego-ev3-touch"; 144 | static constexpr char ev3_color[] = "lego-ev3-color"; 145 | static constexpr char ev3_ultrasonic[] = "lego-ev3-us"; 146 | static constexpr char ev3_gyro[] = "lego-ev3-gyro"; 147 | static constexpr char ev3_infrared[] = "lego-ev3-ir"; 148 | 149 | static constexpr char nxt_touch[] = "lego-nxt-touch"; 150 | static constexpr char nxt_light[] = "lego-nxt-light"; 151 | static constexpr char nxt_sound[] = "lego-nxt-sound"; 152 | static constexpr char nxt_ultrasonic[] = "lego-nxt-us"; 153 | static constexpr char nxt_i2c_sensor[] = "nxt-i2c-sensor"; 154 | static constexpr char nxt_analog[] = "nxt-analog"; 155 | 156 | sensor(address_type); 157 | sensor(address_type, const std::set&); 158 | 159 | using device::connected; 160 | using device::device_index; 161 | 162 | // Returns the value or values measured by the sensor. Check `num_values` to 163 | // see how many values there are. Values with index >= num_values will return 164 | // an error. The values are fixed point numbers, so check `decimals` to see 165 | // if you need to divide to get the actual value. 166 | int value(unsigned index=0) const; 167 | 168 | // The value converted to float using `decimals`. 169 | float float_value(unsigned index=0) const; 170 | 171 | // Human-readable name of the connected sensor. 172 | std::string type_name() const; 173 | 174 | // Bin Data Format: read-only 175 | // Returns the format of the values in `bin_data` for the current mode. 176 | // Possible values are: 177 | // 178 | // - `u8`: Unsigned 8-bit integer (byte) 179 | // - `s8`: Signed 8-bit integer (sbyte) 180 | // - `u16`: Unsigned 16-bit integer (ushort) 181 | // - `s16`: Signed 16-bit integer (short) 182 | // - `s16_be`: Signed 16-bit integer, big endian 183 | // - `s32`: Signed 32-bit integer (int) 184 | // - `float`: IEEE 754 32-bit floating point (float) 185 | std::string bin_data_format() const { return get_attr_string("bin_data_format"); }; 186 | 187 | // Bin Data: read-only 188 | // Returns the unscaled raw values in the `value` attributes as raw byte 189 | // array. Use `bin_data_format`, `num_values` and the individual sensor 190 | // documentation to determine how to interpret the data. 191 | const std::vector& bin_data() const; 192 | 193 | // Bin Data: read-only 194 | // Writes the unscaled raw values in the `value` attributes into the 195 | // user-provided struct/buffer. Use `bin_data_format`, `num_values` and the 196 | // individual sensor documentation to determine how to interpret the data. 197 | template 198 | void bin_data(T *buf) const { 199 | bin_data(); // fills _bin_data 200 | std::copy_n(_bin_data.data(), _bin_data.size(), reinterpret_cast(buf)); 201 | } 202 | 203 | // Address: read-only 204 | // Returns the name of the port that the sensor is connected to, e.g. `ev3:in1`. 205 | // I2C sensors also include the I2C address (decimal), e.g. `ev3:in1:i2c8`. 206 | std::string address() const { return get_attr_string("address"); } 207 | 208 | // Command: write-only 209 | // Sends a command to the sensor. 210 | sensor& set_command(std::string v) { 211 | set_attr_string("command", v); 212 | return *this; 213 | } 214 | 215 | // Commands: read-only 216 | // Returns a list of the valid commands for the sensor. 217 | // Returns -EOPNOTSUPP if no commands are supported. 218 | mode_set commands() const { return get_attr_set("commands"); } 219 | 220 | // Decimals: read-only 221 | // Returns the number of decimal places for the values in the `value` 222 | // attributes of the current mode. 223 | int decimals() const { return get_attr_int("decimals"); } 224 | 225 | // Driver Name: read-only 226 | // Returns the name of the sensor device/driver. See the list of [supported 227 | // sensors] for a complete list of drivers. 228 | std::string driver_name() const { return get_attr_string("driver_name"); } 229 | 230 | // Mode: read/write 231 | // Returns the current mode. Writing one of the values returned by `modes` 232 | // sets the sensor to that mode. 233 | std::string mode() const { return get_attr_string("mode"); } 234 | sensor& set_mode(std::string v) { 235 | set_attr_string("mode", v); 236 | return *this; 237 | } 238 | 239 | // Modes: read-only 240 | // Returns a list of the valid modes for the sensor. 241 | mode_set modes() const { return get_attr_set("modes"); } 242 | 243 | // Num Values: read-only 244 | // Returns the number of `value` attributes that will return a valid value 245 | // for the current mode. 246 | int num_values() const { return get_attr_int("num_values"); } 247 | 248 | // Units: read-only 249 | // Returns the units of the measured value for the current mode. May return 250 | // empty string 251 | std::string units() const { return get_attr_string("units"); } 252 | 253 | protected: 254 | sensor() {} 255 | 256 | bool connect(const std::map>&) noexcept; 257 | 258 | mutable std::vector _bin_data; 259 | }; 260 | 261 | //----------------------------------------------------------------------------- 262 | // A generic interface to control I2C-type EV3 sensors. 263 | //----------------------------------------------------------------------------- 264 | class i2c_sensor : public sensor { 265 | public: 266 | i2c_sensor( 267 | address_type address = INPUT_AUTO, 268 | const std::set &types = {} 269 | ); 270 | 271 | // FW Version: read-only 272 | // Returns the firmware version of the sensor if available. Currently only 273 | // I2C/NXT sensors support this. 274 | std::string fw_version() const { return get_attr_string("fw_version"); } 275 | 276 | // Poll MS: read/write 277 | // Returns the polling period of the sensor in milliseconds. Writing sets the 278 | // polling period. Setting to 0 disables polling. Minimum value is hard 279 | // coded as 50 msec. Returns -EOPNOTSUPP if changing polling is not supported. 280 | // Currently only I2C/NXT sensors support changing the polling period. 281 | int poll_ms() const { return get_attr_int("poll_ms"); } 282 | i2c_sensor& set_poll_ms(int v) { 283 | set_attr_int("poll_ms", v); 284 | return *this; 285 | } 286 | }; 287 | 288 | //----------------------------------------------------------------------------- 289 | // Touch Sensor 290 | //----------------------------------------------------------------------------- 291 | class touch_sensor : public sensor { 292 | public: 293 | touch_sensor(address_type address = INPUT_AUTO); 294 | 295 | // Button state 296 | static constexpr char mode_touch[] = "TOUCH"; 297 | 298 | 299 | // A boolean indicating whether the current touch sensor is being 300 | // pressed. 301 | bool is_pressed(bool do_set_mode = true) { 302 | if (do_set_mode) set_mode(mode_touch); 303 | return value(0); 304 | } 305 | }; 306 | 307 | //----------------------------------------------------------------------------- 308 | // LEGO EV3 color sensor. 309 | //----------------------------------------------------------------------------- 310 | class color_sensor : public sensor { 311 | public: 312 | color_sensor(address_type address = INPUT_AUTO); 313 | 314 | // Reflected light. Red LED on. 315 | static constexpr char mode_col_reflect[] = "COL-REFLECT"; 316 | 317 | // Ambient light. Red LEDs off. 318 | static constexpr char mode_col_ambient[] = "COL-AMBIENT"; 319 | 320 | // Color. All LEDs rapidly cycling, appears white. 321 | static constexpr char mode_col_color[] = "COL-COLOR"; 322 | 323 | // Raw reflected. Red LED on 324 | static constexpr char mode_ref_raw[] = "REF-RAW"; 325 | 326 | // Raw Color Components. All LEDs rapidly cycling, appears white. 327 | static constexpr char mode_rgb_raw[] = "RGB-RAW"; 328 | 329 | // No color. 330 | static constexpr char color_nocolor[] = "NoColor"; 331 | 332 | // Black color. 333 | static constexpr char color_black[] = "Black"; 334 | 335 | // Blue color. 336 | static constexpr char color_blue[] = "Blue"; 337 | 338 | // Green color. 339 | static constexpr char color_green[] = "Green"; 340 | 341 | // Yellow color. 342 | static constexpr char color_yellow[] = "Yellow"; 343 | 344 | // Red color. 345 | static constexpr char color_red[] = "Red"; 346 | 347 | // White color. 348 | static constexpr char color_white[] = "White"; 349 | 350 | // Brown color. 351 | static constexpr char color_brown[] = "Brown"; 352 | 353 | 354 | // Reflected light intensity as a percentage. Light on sensor is red. 355 | int reflected_light_intensity(bool do_set_mode = true) { 356 | if (do_set_mode) set_mode(mode_col_reflect); 357 | return value(0); 358 | } 359 | 360 | // Ambient light intensity. Light on sensor is dimly lit blue. 361 | int ambient_light_intensity(bool do_set_mode = true) { 362 | if (do_set_mode) set_mode(mode_col_ambient); 363 | return value(0); 364 | } 365 | 366 | // Color detected by the sensor, categorized by overall value. 367 | // - 0: No color 368 | // - 1: Black 369 | // - 2: Blue 370 | // - 3: Green 371 | // - 4: Yellow 372 | // - 5: Red 373 | // - 6: White 374 | // - 7: Brown 375 | int color(bool do_set_mode = true) { 376 | if (do_set_mode) set_mode(mode_col_color); 377 | return value(0); 378 | } 379 | 380 | // Red, green, and blue components of the detected color, in the range 0-1020. 381 | std::tuple raw(bool do_set_mode = true) { 382 | if (do_set_mode) set_mode(mode_rgb_raw); 383 | return std::make_tuple( value(0), value(1), value(2) ); 384 | } 385 | 386 | // Red component of the detected color, in the range 0-1020. 387 | int red(bool do_set_mode = true) { 388 | if (do_set_mode) set_mode(mode_rgb_raw); 389 | return value(0); 390 | } 391 | 392 | // Green component of the detected color, in the range 0-1020. 393 | int green(bool do_set_mode = true) { 394 | if (do_set_mode) set_mode(mode_rgb_raw); 395 | return value(1); 396 | } 397 | 398 | // Blue component of the detected color, in the range 0-1020. 399 | int blue(bool do_set_mode = true) { 400 | if (do_set_mode) set_mode(mode_rgb_raw); 401 | return value(2); 402 | } 403 | }; 404 | 405 | //----------------------------------------------------------------------------- 406 | // LEGO EV3 ultrasonic sensor. 407 | //----------------------------------------------------------------------------- 408 | class ultrasonic_sensor : public sensor { 409 | public: 410 | ultrasonic_sensor(address_type address = INPUT_AUTO); 411 | 412 | ultrasonic_sensor(address_type address, const std::set& sensorTypes); 413 | 414 | // Continuous measurement in centimeters. 415 | static constexpr char mode_us_dist_cm[] = "US-DIST-CM"; 416 | 417 | // Continuous measurement in inches. 418 | static constexpr char mode_us_dist_in[] = "US-DIST-IN"; 419 | 420 | // Listen. 421 | static constexpr char mode_us_listen[] = "US-LISTEN"; 422 | 423 | // Single measurement in centimeters. 424 | static constexpr char mode_us_si_cm[] = "US-SI-CM"; 425 | 426 | // Single measurement in inches. 427 | static constexpr char mode_us_si_in[] = "US-SI-IN"; 428 | 429 | 430 | // Measurement of the distance detected by the sensor, 431 | // in centimeters. 432 | float distance_centimeters(bool do_set_mode = true) { 433 | if (do_set_mode) set_mode(mode_us_dist_cm); 434 | return float_value(0); 435 | } 436 | 437 | // Measurement of the distance detected by the sensor, 438 | // in inches. 439 | float distance_inches(bool do_set_mode = true) { 440 | if (do_set_mode) set_mode(mode_us_dist_in); 441 | return float_value(0); 442 | } 443 | 444 | // Value indicating whether another ultrasonic sensor could 445 | // be heard nearby. 446 | bool other_sensor_present(bool do_set_mode = true) { 447 | if (do_set_mode) set_mode(mode_us_listen); 448 | return value(0); 449 | } 450 | }; 451 | 452 | //----------------------------------------------------------------------------- 453 | // LEGO EV3 gyro sensor. 454 | //----------------------------------------------------------------------------- 455 | class gyro_sensor : public sensor { 456 | public: 457 | gyro_sensor(address_type address = INPUT_AUTO); 458 | 459 | // Angle 460 | static char mode_gyro_ang[]; 461 | 462 | // Rotational speed 463 | static char mode_gyro_rate[]; 464 | 465 | // Raw sensor value 466 | static char mode_gyro_fas[]; 467 | 468 | // Angle and rotational speed 469 | static char mode_gyro_g_a[]; 470 | 471 | // Calibration ??? 472 | static char mode_gyro_cal[]; 473 | 474 | // Tilt rotational speed 475 | static char mode_tilt_rate[]; 476 | 477 | // Tilt angle 478 | static char mode_tilt_ang[]; 479 | 480 | 481 | // The number of degrees that the sensor has been rotated 482 | // since it was put into this mode. 483 | int angle(bool do_set_mode = true) { 484 | if (do_set_mode) set_mode(mode_gyro_ang); 485 | return value(0); 486 | } 487 | 488 | // The rate at which the sensor is rotating, in degrees/second. 489 | int rate(bool do_set_mode = true) { 490 | if (do_set_mode) set_mode(mode_gyro_rate); 491 | return value(0); 492 | } 493 | 494 | // Angle (degrees) and Rotational Speed (degrees/second). 495 | std::tuple rate_and_angle(bool do_set_mode = true) { 496 | if (do_set_mode) set_mode(mode_gyro_g_a); 497 | return std::make_tuple( value(0), value(1) ); 498 | } 499 | 500 | int tilt_angle(bool do_set_mode = true) { 501 | if (do_set_mode) set_mode(mode_tilt_ang); 502 | return value(0); 503 | } 504 | 505 | // The rate at which the sensor is rotating, in degrees/second. 506 | int tilt_rate(bool do_set_mode = true) { 507 | if (do_set_mode) set_mode(mode_tilt_rate); 508 | return value(0); 509 | } 510 | }; 511 | 512 | //----------------------------------------------------------------------------- 513 | // LEGO EV3 infrared sensor. 514 | //----------------------------------------------------------------------------- 515 | class infrared_sensor : public sensor { 516 | public: 517 | infrared_sensor(address_type address = INPUT_AUTO); 518 | 519 | // Proximity 520 | static char mode_ir_prox[]; 521 | 522 | // IR Seeker 523 | static char mode_ir_seek[]; 524 | 525 | // IR Remote Control 526 | static char mode_ir_remote[]; 527 | 528 | // IR Remote Control. State of the buttons is coded in binary 529 | static char mode_ir_rem_a[]; 530 | 531 | // Calibration ??? 532 | static char mode_ir_cal[]; 533 | 534 | 535 | // A measurement of the distance between the sensor and the remote, 536 | // as a percentage. 100% is approximately 70cm/27in. 537 | int proximity(bool do_set_mode = true) { 538 | if (do_set_mode) set_mode(mode_ir_prox); 539 | return value(0); 540 | } 541 | }; 542 | 543 | //----------------------------------------------------------------------------- 544 | // LEGO NXT Sound Sensor 545 | //----------------------------------------------------------------------------- 546 | class sound_sensor : public sensor { 547 | public: 548 | sound_sensor(address_type address = INPUT_AUTO); 549 | 550 | // Sound pressure level. Flat weighting 551 | static char mode_db[]; 552 | 553 | // Sound pressure level. A weighting 554 | static char mode_dba[]; 555 | 556 | 557 | // A measurement of the measured sound pressure level, as a 558 | // percent. Uses a flat weighting. 559 | float sound_pressure(bool do_set_mode = true) { 560 | if (do_set_mode) set_mode(mode_db); 561 | return float_value(0); 562 | } 563 | 564 | // A measurement of the measured sound pressure level, as a 565 | // percent. Uses A-weighting, which focuses on levels up to 55 dB. 566 | float sound_pressure_low(bool do_set_mode = true) { 567 | if (do_set_mode) set_mode(mode_dba); 568 | return float_value(0); 569 | } 570 | }; 571 | 572 | //----------------------------------------------------------------------------- 573 | // LEGO NXT Light Sensor 574 | //----------------------------------------------------------------------------- 575 | class light_sensor : public sensor { 576 | public: 577 | light_sensor(address_type address = INPUT_AUTO); 578 | 579 | // Reflected light. LED on 580 | static char mode_reflect[]; 581 | 582 | // Ambient light. LED off 583 | static char mode_ambient[]; 584 | 585 | 586 | // A measurement of the reflected light intensity, as a percentage. 587 | float reflected_light_intensity(bool do_set_mode = true) { 588 | if (do_set_mode) set_mode(mode_reflect); 589 | return float_value(0); 590 | } 591 | 592 | // A measurement of the ambient light intensity, as a percentage. 593 | float ambient_light_intensity(bool do_set_mode = true) { 594 | if (do_set_mode) set_mode(mode_ambient); 595 | return float_value(0); 596 | } 597 | }; 598 | 599 | //----------------------------------------------------------------------------- 600 | // The motor class provides a uniform interface for using motors with 601 | // positional and directional feedback such as the EV3 and NXT motors. 602 | // This feedback allows for precise control of the motors. This is the 603 | // most common type of motor, so we just call it `motor`. 604 | // 605 | // The way to configure a motor is to set the '_sp' attributes when 606 | // calling a command or before. Only in 'run_direct' mode attribute 607 | // changes are processed immediately, in the other modes they only 608 | // take place when a new command is issued. 609 | //----------------------------------------------------------------------------- 610 | class motor : protected device { 611 | public: 612 | typedef device_type motor_type; 613 | 614 | motor(address_type); 615 | motor(address_type, const motor_type&); 616 | 617 | static char motor_large[]; 618 | static char motor_medium[]; 619 | static char motor_nxt[]; 620 | 621 | using device::connected; 622 | using device::device_index; 623 | 624 | // Run the motor until another command is sent. 625 | static char command_run_forever[]; 626 | 627 | // Run to an absolute position specified by `position_sp` and then 628 | // stop using the action specified in `stop_action`. 629 | static char command_run_to_abs_pos[]; 630 | 631 | // Run to a position relative to the current `position` value. 632 | // The new position will be current `position` + `position_sp`. 633 | // When the new position is reached, the motor will stop using 634 | // the action specified by `stop_action`. 635 | static char command_run_to_rel_pos[]; 636 | 637 | // Run the motor for the amount of time specified in `time_sp` 638 | // and then stop the motor using the action specified by `stop_action`. 639 | static char command_run_timed[]; 640 | 641 | // Run the motor at the duty cycle specified by `duty_cycle_sp`. 642 | // Unlike other run commands, changing `duty_cycle_sp` while running *will* 643 | // take effect immediately. 644 | static char command_run_direct[]; 645 | 646 | // Stop any of the run commands before they are complete using the 647 | // action specified by `stop_action`. 648 | static char command_stop[]; 649 | 650 | // Reset all of the motor parameter attributes to their default value. 651 | // This will also have the effect of stopping the motor. 652 | static char command_reset[]; 653 | 654 | // Sets the normal polarity of the rotary encoder. 655 | static char encoder_polarity_normal[]; 656 | 657 | // Sets the inversed polarity of the rotary encoder. 658 | static char encoder_polarity_inversed[]; 659 | 660 | // With `normal` polarity, a positive duty cycle will 661 | // cause the motor to rotate clockwise. 662 | static char polarity_normal[]; 663 | 664 | // With `inversed` polarity, a positive duty cycle will 665 | // cause the motor to rotate counter-clockwise. 666 | static char polarity_inversed[]; 667 | 668 | // Power is being sent to the motor. 669 | static char state_running[]; 670 | 671 | // The motor is ramping up or down and has not yet reached a constant output level. 672 | static char state_ramping[]; 673 | 674 | // The motor is not turning, but rather attempting to hold a fixed position. 675 | static char state_holding[]; 676 | 677 | // The motor is turning, but cannot reach its `speed_sp`. 678 | static char state_overloaded[]; 679 | 680 | // The motor is not turning when it should be. 681 | static char state_stalled[]; 682 | 683 | // Power will be removed from the motor and it will freely coast to a stop. 684 | static char stop_action_coast[]; 685 | 686 | // Power will be removed from the motor and a passive electrical load will 687 | // be placed on the motor. This is usually done by shorting the motor terminals 688 | // together. This load will absorb the energy from the rotation of the motors and 689 | // cause the motor to stop more quickly than coasting. 690 | static char stop_action_brake[]; 691 | 692 | // Does not remove power from the motor. Instead it actively try to hold the motor 693 | // at the current position. If an external force tries to turn the motor, the motor 694 | // will `push back` to maintain its position. 695 | static char stop_action_hold[]; 696 | 697 | // Address: read-only 698 | // Returns the name of the port that this motor is connected to. 699 | std::string address() const { return get_attr_string("address"); } 700 | 701 | // Command: write-only 702 | // Sends a command to the motor controller. See `commands` for a list of 703 | // possible values. 704 | motor& set_command(std::string v) { 705 | set_attr_string("command", v); 706 | return *this; 707 | } 708 | 709 | // Commands: read-only 710 | // Returns a list of commands that are supported by the motor 711 | // controller. Possible values are `run-forever`, `run-to-abs-pos`, `run-to-rel-pos`, 712 | // `run-timed`, `run-direct`, `stop` and `reset`. Not all commands may be supported. 713 | // 714 | // - `run-forever` will cause the motor to run until another command is sent. 715 | // - `run-to-abs-pos` will run to an absolute position specified by `position_sp` 716 | // and then stop using the action specified in `stop_action`. 717 | // - `run-to-rel-pos` will run to a position relative to the current `position` value. 718 | // The new position will be current `position` + `position_sp`. When the new 719 | // position is reached, the motor will stop using the action specified by `stop_action`. 720 | // - `run-timed` will run the motor for the amount of time specified in `time_sp` 721 | // and then stop the motor using the action specified by `stop_action`. 722 | // - `run-direct` will run the motor at the duty cycle specified by `duty_cycle_sp`. 723 | // Unlike other run commands, changing `duty_cycle_sp` while running *will* 724 | // take effect immediately. 725 | // - `stop` will stop any of the run commands before they are complete using the 726 | // action specified by `stop_action`. 727 | // - `reset` will reset all of the motor parameter attributes to their default value. 728 | // This will also have the effect of stopping the motor. 729 | mode_set commands() const { return get_attr_set("commands"); } 730 | 731 | // Count Per Rot: read-only 732 | // Returns the number of tacho counts in one rotation of the motor. Tacho counts 733 | // are used by the position and speed attributes, so you can use this value 734 | // to convert rotations or degrees to tacho counts. (rotation motors only) 735 | int count_per_rot() const { return get_attr_int("count_per_rot"); } 736 | 737 | // Count Per M: read-only 738 | // Returns the number of tacho counts in one meter of travel of the motor. Tacho 739 | // counts are used by the position and speed attributes, so you can use this 740 | // value to convert from distance to tacho counts. (linear motors only) 741 | int count_per_m() const { return get_attr_int("count_per_m"); } 742 | 743 | // Driver Name: read-only 744 | // Returns the name of the driver that provides this tacho motor device. 745 | std::string driver_name() const { return get_attr_string("driver_name"); } 746 | 747 | // Duty Cycle: read-only 748 | // Returns the current duty cycle of the motor. Units are percent. Values 749 | // are -100 to 100. 750 | int duty_cycle() const { return get_attr_int("duty_cycle"); } 751 | 752 | // Duty Cycle SP: read/write 753 | // Writing sets the duty cycle setpoint. Reading returns the current value. 754 | // Units are in percent. Valid values are -100 to 100. A negative value causes 755 | // the motor to rotate in reverse. 756 | int duty_cycle_sp() const { return get_attr_int("duty_cycle_sp"); } 757 | motor& set_duty_cycle_sp(int v) { 758 | set_attr_int("duty_cycle_sp", v); 759 | return *this; 760 | } 761 | 762 | // Full Travel Count: read-only 763 | // Returns the number of tacho counts in the full travel of the motor. When 764 | // combined with the `count_per_m` atribute, you can use this value to 765 | // calculate the maximum travel distance of the motor. (linear motors only) 766 | int full_travel_count() const { return get_attr_int("full_travel_count"); } 767 | 768 | // Polarity: read/write 769 | // Sets the polarity of the motor. With `normal` polarity, a positive duty 770 | // cycle will cause the motor to rotate clockwise. With `inversed` polarity, 771 | // a positive duty cycle will cause the motor to rotate counter-clockwise. 772 | // Valid values are `normal` and `inversed`. 773 | std::string polarity() const { return get_attr_string("polarity"); } 774 | motor& set_polarity(std::string v) { 775 | set_attr_string("polarity", v); 776 | return *this; 777 | } 778 | 779 | // Position: read/write 780 | // Returns the current position of the motor in pulses of the rotary 781 | // encoder. When the motor rotates clockwise, the position will increase. 782 | // Likewise, rotating counter-clockwise causes the position to decrease. 783 | // Writing will set the position to that value. 784 | int position() const { return get_attr_int("position"); } 785 | motor& set_position(int v) { 786 | set_attr_int("position", v); 787 | return *this; 788 | } 789 | 790 | // Position P: read/write 791 | // The proportional constant for the position PID. 792 | int position_p() const { return get_attr_int("hold_pid/Kp"); } 793 | motor& set_position_p(int v) { 794 | set_attr_int("hold_pid/Kp", v); 795 | return *this; 796 | } 797 | 798 | // Position I: read/write 799 | // The integral constant for the position PID. 800 | int position_i() const { return get_attr_int("hold_pid/Ki"); } 801 | motor& set_position_i(int v) { 802 | set_attr_int("hold_pid/Ki", v); 803 | return *this; 804 | } 805 | 806 | // Position D: read/write 807 | // The derivative constant for the position PID. 808 | int position_d() const { return get_attr_int("hold_pid/Kd"); } 809 | motor& set_position_d(int v) { 810 | set_attr_int("hold_pid/Kd", v); 811 | return *this; 812 | } 813 | 814 | // Position SP: read/write 815 | // Writing specifies the target position for the `run-to-abs-pos` and `run-to-rel-pos` 816 | // commands. Reading returns the current value. Units are in tacho counts. You 817 | // can use the value returned by `counts_per_rot` to convert tacho counts to/from 818 | // rotations or degrees. 819 | int position_sp() const { return get_attr_int("position_sp"); } 820 | motor& set_position_sp(int v) { 821 | set_attr_int("position_sp", v); 822 | return *this; 823 | } 824 | 825 | // Max Speed: read-only 826 | // Returns the maximum value that is accepted by the `speed_sp` attribute. This 827 | // may be slightly different than the maximum speed that a particular motor can 828 | // reach - it's the maximum theoretical speed. 829 | int max_speed() const { return get_attr_int("max_speed"); } 830 | 831 | // Speed: read-only 832 | // Returns the current motor speed in tacho counts per second. Note, this is 833 | // not necessarily degrees (although it is for LEGO motors). Use the `count_per_rot` 834 | // attribute to convert this value to RPM or deg/sec. 835 | int speed() const { return get_attr_int("speed"); } 836 | 837 | // Speed SP: read/write 838 | // Writing sets the target speed in tacho counts per second used for all `run-*` 839 | // commands except `run-direct`. Reading returns the current value. A negative 840 | // value causes the motor to rotate in reverse with the exception of `run-to-*-pos` 841 | // commands where the sign is ignored. Use the `count_per_rot` attribute to convert 842 | // RPM or deg/sec to tacho counts per second. Use the `count_per_m` attribute to 843 | // convert m/s to tacho counts per second. 844 | int speed_sp() const { return get_attr_int("speed_sp"); } 845 | motor& set_speed_sp(int v) { 846 | set_attr_int("speed_sp", v); 847 | return *this; 848 | } 849 | 850 | // Ramp Up SP: read/write 851 | // Writing sets the ramp up setpoint. Reading returns the current value. Units 852 | // are in milliseconds and must be positive. When set to a non-zero value, the 853 | // motor speed will increase from 0 to 100% of `max_speed` over the span of this 854 | // setpoint. The actual ramp time is the ratio of the difference between the 855 | // `speed_sp` and the current `speed` and max_speed multiplied by `ramp_up_sp`. 856 | int ramp_up_sp() const { return get_attr_int("ramp_up_sp"); } 857 | motor& set_ramp_up_sp(int v) { 858 | set_attr_int("ramp_up_sp", v); 859 | return *this; 860 | } 861 | 862 | // Ramp Down SP: read/write 863 | // Writing sets the ramp down setpoint. Reading returns the current value. Units 864 | // are in milliseconds and must be positive. When set to a non-zero value, the 865 | // motor speed will decrease from 0 to 100% of `max_speed` over the span of this 866 | // setpoint. The actual ramp time is the ratio of the difference between the 867 | // `speed_sp` and the current `speed` and max_speed multiplied by `ramp_down_sp`. 868 | int ramp_down_sp() const { return get_attr_int("ramp_down_sp"); } 869 | motor& set_ramp_down_sp(int v) { 870 | set_attr_int("ramp_down_sp", v); 871 | return *this; 872 | } 873 | 874 | // Speed P: read/write 875 | // The proportional constant for the speed regulation PID. 876 | int speed_p() const { return get_attr_int("speed_pid/Kp"); } 877 | motor& set_speed_p(int v) { 878 | set_attr_int("speed_pid/Kp", v); 879 | return *this; 880 | } 881 | 882 | // Speed I: read/write 883 | // The integral constant for the speed regulation PID. 884 | int speed_i() const { return get_attr_int("speed_pid/Ki"); } 885 | motor& set_speed_i(int v) { 886 | set_attr_int("speed_pid/Ki", v); 887 | return *this; 888 | } 889 | 890 | // Speed D: read/write 891 | // The derivative constant for the speed regulation PID. 892 | int speed_d() const { return get_attr_int("speed_pid/Kd"); } 893 | motor& set_speed_d(int v) { 894 | set_attr_int("speed_pid/Kd", v); 895 | return *this; 896 | } 897 | 898 | // State: read-only 899 | // Reading returns a list of state flags. Possible flags are 900 | // `running`, `ramping`, `holding`, `overloaded` and `stalled`. 901 | mode_set state() const { return get_attr_set("state"); } 902 | 903 | // Stop Action: read/write 904 | // Reading returns the current stop action. Writing sets the stop action. 905 | // The value determines the motors behavior when `command` is set to `stop`. 906 | // Also, it determines the motors behavior when a run command completes. See 907 | // `stop_actions` for a list of possible values. 908 | std::string stop_action() const { return get_attr_string("stop_action"); } 909 | motor& set_stop_action(std::string v) { 910 | set_attr_string("stop_action", v); 911 | return *this; 912 | } 913 | 914 | // Stop Actions: read-only 915 | // Returns a list of stop actions supported by the motor controller. 916 | // Possible values are `coast`, `brake` and `hold`. `coast` means that power will 917 | // be removed from the motor and it will freely coast to a stop. `brake` means 918 | // that power will be removed from the motor and a passive electrical load will 919 | // be placed on the motor. This is usually done by shorting the motor terminals 920 | // together. This load will absorb the energy from the rotation of the motors and 921 | // cause the motor to stop more quickly than coasting. `hold` does not remove 922 | // power from the motor. Instead it actively tries to hold the motor at the current 923 | // position. If an external force tries to turn the motor, the motor will 'push 924 | // back' to maintain its position. 925 | mode_set stop_actions() const { return get_attr_set("stop_actions"); } 926 | 927 | // Time SP: read/write 928 | // Writing specifies the amount of time the motor will run when using the 929 | // `run-timed` command. Reading returns the current value. Units are in 930 | // milliseconds. 931 | int time_sp() const { return get_attr_int("time_sp"); } 932 | motor& set_time_sp(int v) { 933 | set_attr_int("time_sp", v); 934 | return *this; 935 | } 936 | 937 | // Run the motor until another command is sent. 938 | void run_forever() { set_command("run-forever"); } 939 | 940 | // Run to an absolute position specified by `position_sp` and then 941 | // stop using the action specified in `stop_action`. 942 | void run_to_abs_pos() { set_command("run-to-abs-pos"); } 943 | 944 | // Run to a position relative to the current `position` value. 945 | // The new position will be current `position` + `position_sp`. 946 | // When the new position is reached, the motor will stop using 947 | // the action specified by `stop_action`. 948 | void run_to_rel_pos() { set_command("run-to-rel-pos"); } 949 | 950 | // Run the motor for the amount of time specified in `time_sp` 951 | // and then stop the motor using the action specified by `stop_action`. 952 | void run_timed() { set_command("run-timed"); } 953 | 954 | // Run the motor at the duty cycle specified by `duty_cycle_sp`. 955 | // Unlike other run commands, changing `duty_cycle_sp` while running *will* 956 | // take effect immediately. 957 | void run_direct() { set_command("run-direct"); } 958 | 959 | // Stop any of the run commands before they are complete using the 960 | // action specified by `stop_action`. 961 | void stop() { set_command("stop"); } 962 | 963 | // Reset all of the motor parameter attributes to their default value. 964 | // This will also have the effect of stopping the motor. 965 | void reset() { set_command("reset"); } 966 | 967 | protected: 968 | motor() {} 969 | 970 | bool connect(const std::map>&) noexcept; 971 | }; 972 | 973 | //----------------------------------------------------------------------------- 974 | // EV3 medium motor 975 | //----------------------------------------------------------------------------- 976 | class medium_motor : public motor { 977 | public: 978 | medium_motor(address_type address = OUTPUT_AUTO); 979 | }; 980 | 981 | //----------------------------------------------------------------------------- 982 | // EV3 large motor 983 | //----------------------------------------------------------------------------- 984 | class large_motor : public motor { 985 | public: 986 | large_motor(address_type address = OUTPUT_AUTO); 987 | }; 988 | 989 | //----------------------------------------------------------------------------- 990 | // NXT motor 991 | //----------------------------------------------------------------------------- 992 | class nxt_motor : public motor { 993 | public: 994 | nxt_motor(address_type address = OUTPUT_AUTO); 995 | }; 996 | 997 | //----------------------------------------------------------------------------- 998 | // The DC motor class provides a uniform interface for using regular DC motors 999 | // with no fancy controls or feedback. This includes LEGO MINDSTORMS RCX motors 1000 | // and LEGO Power Functions motors. 1001 | //----------------------------------------------------------------------------- 1002 | class dc_motor : protected device { 1003 | public: 1004 | dc_motor(address_type address = OUTPUT_AUTO); 1005 | 1006 | using device::connected; 1007 | using device::device_index; 1008 | 1009 | // Run the motor until another command is sent. 1010 | static char command_run_forever[]; 1011 | 1012 | // Run the motor for the amount of time specified in `time_sp` 1013 | // and then stop the motor using the action specified by `stop_action`. 1014 | static char command_run_timed[]; 1015 | 1016 | // Run the motor at the duty cycle specified by `duty_cycle_sp`. 1017 | // Unlike other run commands, changing `duty_cycle_sp` while running *will* 1018 | // take effect immediately. 1019 | static char command_run_direct[]; 1020 | 1021 | // Stop any of the run commands before they are complete using the 1022 | // action specified by `stop_action`. 1023 | static char command_stop[]; 1024 | 1025 | // With `normal` polarity, a positive duty cycle will 1026 | // cause the motor to rotate clockwise. 1027 | static char polarity_normal[]; 1028 | 1029 | // With `inversed` polarity, a positive duty cycle will 1030 | // cause the motor to rotate counter-clockwise. 1031 | static char polarity_inversed[]; 1032 | 1033 | // Power will be removed from the motor and it will freely coast to a stop. 1034 | static char stop_action_coast[]; 1035 | 1036 | // Power will be removed from the motor and a passive electrical load will 1037 | // be placed on the motor. This is usually done by shorting the motor terminals 1038 | // together. This load will absorb the energy from the rotation of the motors and 1039 | // cause the motor to stop more quickly than coasting. 1040 | static char stop_action_brake[]; 1041 | 1042 | // Address: read-only 1043 | // Returns the name of the port that this motor is connected to. 1044 | std::string address() const { return get_attr_string("address"); } 1045 | 1046 | // Command: write-only 1047 | // Sets the command for the motor. Possible values are `run-forever`, `run-timed` and 1048 | // `stop`. Not all commands may be supported, so be sure to check the contents 1049 | // of the `commands` attribute. 1050 | dc_motor& set_command(std::string v) { 1051 | set_attr_string("command", v); 1052 | return *this; 1053 | } 1054 | 1055 | // Commands: read-only 1056 | // Returns a list of commands supported by the motor 1057 | // controller. 1058 | mode_set commands() const { return get_attr_set("commands"); } 1059 | 1060 | // Driver Name: read-only 1061 | // Returns the name of the motor driver that loaded this device. See the list 1062 | // of [supported devices] for a list of drivers. 1063 | std::string driver_name() const { return get_attr_string("driver_name"); } 1064 | 1065 | // Duty Cycle: read-only 1066 | // Shows the current duty cycle of the PWM signal sent to the motor. Values 1067 | // are -100 to 100 (-100% to 100%). 1068 | int duty_cycle() const { return get_attr_int("duty_cycle"); } 1069 | 1070 | // Duty Cycle SP: read/write 1071 | // Writing sets the duty cycle setpoint of the PWM signal sent to the motor. 1072 | // Valid values are -100 to 100 (-100% to 100%). Reading returns the current 1073 | // setpoint. 1074 | int duty_cycle_sp() const { return get_attr_int("duty_cycle_sp"); } 1075 | dc_motor& set_duty_cycle_sp(int v) { 1076 | set_attr_int("duty_cycle_sp", v); 1077 | return *this; 1078 | } 1079 | 1080 | // Polarity: read/write 1081 | // Sets the polarity of the motor. Valid values are `normal` and `inversed`. 1082 | std::string polarity() const { return get_attr_string("polarity"); } 1083 | dc_motor& set_polarity(std::string v) { 1084 | set_attr_string("polarity", v); 1085 | return *this; 1086 | } 1087 | 1088 | // Ramp Down SP: read/write 1089 | // Sets the time in milliseconds that it take the motor to ramp down from 100% 1090 | // to 0%. Valid values are 0 to 10000 (10 seconds). Default is 0. 1091 | int ramp_down_sp() const { return get_attr_int("ramp_down_sp"); } 1092 | dc_motor& set_ramp_down_sp(int v) { 1093 | set_attr_int("ramp_down_sp", v); 1094 | return *this; 1095 | } 1096 | 1097 | // Ramp Up SP: read/write 1098 | // Sets the time in milliseconds that it take the motor to up ramp from 0% to 1099 | // 100%. Valid values are 0 to 10000 (10 seconds). Default is 0. 1100 | int ramp_up_sp() const { return get_attr_int("ramp_up_sp"); } 1101 | dc_motor& set_ramp_up_sp(int v) { 1102 | set_attr_int("ramp_up_sp", v); 1103 | return *this; 1104 | } 1105 | 1106 | // State: read-only 1107 | // Gets a list of flags indicating the motor status. Possible 1108 | // flags are `running` and `ramping`. `running` indicates that the motor is 1109 | // powered. `ramping` indicates that the motor has not yet reached the 1110 | // `duty_cycle_sp`. 1111 | mode_set state() const { return get_attr_set("state"); } 1112 | 1113 | // Stop Action: write-only 1114 | // Sets the stop action that will be used when the motor stops. Read 1115 | // `stop_actions` to get the list of valid values. 1116 | dc_motor& set_stop_action(std::string v) { 1117 | set_attr_string("stop_action", v); 1118 | return *this; 1119 | } 1120 | 1121 | // Stop Actions: read-only 1122 | // Gets a list of stop actions. Valid values are `coast` 1123 | // and `brake`. 1124 | mode_set stop_actions() const { return get_attr_set("stop_actions"); } 1125 | 1126 | // Time SP: read/write 1127 | // Writing specifies the amount of time the motor will run when using the 1128 | // `run-timed` command. Reading returns the current value. Units are in 1129 | // milliseconds. 1130 | int time_sp() const { return get_attr_int("time_sp"); } 1131 | dc_motor& set_time_sp(int v) { 1132 | set_attr_int("time_sp", v); 1133 | return *this; 1134 | } 1135 | 1136 | 1137 | // Run the motor until another command is sent. 1138 | void run_forever() { set_command("run-forever"); } 1139 | 1140 | // Run the motor for the amount of time specified in `time_sp` 1141 | // and then stop the motor using the action specified by `stop_action`. 1142 | void run_timed() { set_command("run-timed"); } 1143 | 1144 | // Run the motor at the duty cycle specified by `duty_cycle_sp`. 1145 | // Unlike other run commands, changing `duty_cycle_sp` while running *will* 1146 | // take effect immediately. 1147 | void run_direct() { set_command("run-direct"); } 1148 | 1149 | // Stop any of the run commands before they are complete using the 1150 | // action specified by `stop_action`. 1151 | void stop() { set_command("stop"); } 1152 | 1153 | 1154 | protected: 1155 | std::string _port_name; 1156 | }; 1157 | 1158 | //----------------------------------------------------------------------------- 1159 | // The servo motor class provides a uniform interface for using hobby type 1160 | // servo motors. 1161 | //----------------------------------------------------------------------------- 1162 | class servo_motor : protected device { 1163 | public: 1164 | servo_motor(address_type address = OUTPUT_AUTO); 1165 | 1166 | using device::connected; 1167 | using device::device_index; 1168 | 1169 | // Drive servo to the position set in the `position_sp` attribute. 1170 | static char command_run[]; 1171 | 1172 | // Remove power from the motor. 1173 | static char command_float[]; 1174 | 1175 | // With `normal` polarity, a positive duty cycle will 1176 | // cause the motor to rotate clockwise. 1177 | static char polarity_normal[]; 1178 | 1179 | // With `inversed` polarity, a positive duty cycle will 1180 | // cause the motor to rotate counter-clockwise. 1181 | static char polarity_inversed[]; 1182 | 1183 | // Address: read-only 1184 | // Returns the name of the port that this motor is connected to. 1185 | std::string address() const { return get_attr_string("address"); } 1186 | 1187 | // Command: write-only 1188 | // Sets the command for the servo. Valid values are `run` and `float`. Setting 1189 | // to `run` will cause the servo to be driven to the position_sp set in the 1190 | // `position_sp` attribute. Setting to `float` will remove power from the motor. 1191 | servo_motor& set_command(std::string v) { 1192 | set_attr_string("command", v); 1193 | return *this; 1194 | } 1195 | 1196 | // Driver Name: read-only 1197 | // Returns the name of the motor driver that loaded this device. See the list 1198 | // of [supported devices] for a list of drivers. 1199 | std::string driver_name() const { return get_attr_string("driver_name"); } 1200 | 1201 | // Max Pulse SP: read/write 1202 | // Used to set the pulse size in milliseconds for the signal that tells the 1203 | // servo to drive to the maximum (clockwise) position_sp. Default value is 2400. 1204 | // Valid values are 2300 to 2700. You must write to the position_sp attribute for 1205 | // changes to this attribute to take effect. 1206 | int max_pulse_sp() const { return get_attr_int("max_pulse_sp"); } 1207 | servo_motor& set_max_pulse_sp(int v) { 1208 | set_attr_int("max_pulse_sp", v); 1209 | return *this; 1210 | } 1211 | 1212 | // Mid Pulse SP: read/write 1213 | // Used to set the pulse size in milliseconds for the signal that tells the 1214 | // servo to drive to the mid position_sp. Default value is 1500. Valid 1215 | // values are 1300 to 1700. For example, on a 180 degree servo, this would be 1216 | // 90 degrees. On continuous rotation servo, this is the 'neutral' position_sp 1217 | // where the motor does not turn. You must write to the position_sp attribute for 1218 | // changes to this attribute to take effect. 1219 | int mid_pulse_sp() const { return get_attr_int("mid_pulse_sp"); } 1220 | servo_motor& set_mid_pulse_sp(int v) { 1221 | set_attr_int("mid_pulse_sp", v); 1222 | return *this; 1223 | } 1224 | 1225 | // Min Pulse SP: read/write 1226 | // Used to set the pulse size in milliseconds for the signal that tells the 1227 | // servo to drive to the miniumum (counter-clockwise) position_sp. Default value 1228 | // is 600. Valid values are 300 to 700. You must write to the position_sp 1229 | // attribute for changes to this attribute to take effect. 1230 | int min_pulse_sp() const { return get_attr_int("min_pulse_sp"); } 1231 | servo_motor& set_min_pulse_sp(int v) { 1232 | set_attr_int("min_pulse_sp", v); 1233 | return *this; 1234 | } 1235 | 1236 | // Polarity: read/write 1237 | // Sets the polarity of the servo. Valid values are `normal` and `inversed`. 1238 | // Setting the value to `inversed` will cause the position_sp value to be 1239 | // inversed. i.e `-100` will correspond to `max_pulse_sp`, and `100` will 1240 | // correspond to `min_pulse_sp`. 1241 | std::string polarity() const { return get_attr_string("polarity"); } 1242 | servo_motor& set_polarity(std::string v) { 1243 | set_attr_string("polarity", v); 1244 | return *this; 1245 | } 1246 | 1247 | // Position SP: read/write 1248 | // Reading returns the current position_sp of the servo. Writing instructs the 1249 | // servo to move to the specified position_sp. Units are percent. Valid values 1250 | // are -100 to 100 (-100% to 100%) where `-100` corresponds to `min_pulse_sp`, 1251 | // `0` corresponds to `mid_pulse_sp` and `100` corresponds to `max_pulse_sp`. 1252 | int position_sp() const { return get_attr_int("position_sp"); } 1253 | servo_motor& set_position_sp(int v) { 1254 | set_attr_int("position_sp", v); 1255 | return *this; 1256 | } 1257 | 1258 | // Rate SP: read/write 1259 | // Sets the rate_sp at which the servo travels from 0 to 100.0% (half of the full 1260 | // range of the servo). Units are in milliseconds. Example: Setting the rate_sp 1261 | // to 1000 means that it will take a 180 degree servo 2 second to move from 0 1262 | // to 180 degrees. Note: Some servo controllers may not support this in which 1263 | // case reading and writing will fail with `-EOPNOTSUPP`. In continuous rotation 1264 | // servos, this value will affect the rate_sp at which the speed ramps up or down. 1265 | int rate_sp() const { return get_attr_int("rate_sp"); } 1266 | servo_motor& set_rate_sp(int v) { 1267 | set_attr_int("rate_sp", v); 1268 | return *this; 1269 | } 1270 | 1271 | // State: read-only 1272 | // Returns a list of flags indicating the state of the servo. 1273 | // Possible values are: 1274 | // * `running`: Indicates that the motor is powered. 1275 | mode_set state() const { return get_attr_set("state"); } 1276 | 1277 | 1278 | // Drive servo to the position set in the `position_sp` attribute. 1279 | void run() { set_command("run"); } 1280 | 1281 | // Remove power from the motor. 1282 | void float_() { set_command("float"); } 1283 | }; 1284 | 1285 | //----------------------------------------------------------------------------- 1286 | // Any device controlled by the generic LED driver. 1287 | // See https://www.kernel.org/doc/Documentation/leds/leds-class.txt 1288 | // for more details. 1289 | //----------------------------------------------------------------------------- 1290 | class led : protected device { 1291 | public: 1292 | led(std::string name); 1293 | 1294 | using device::connected; 1295 | 1296 | // Max Brightness: read-only 1297 | // Returns the maximum allowable brightness value. 1298 | int max_brightness() const { return get_attr_int("max_brightness"); } 1299 | 1300 | // Brightness: read/write 1301 | // Sets the brightness level. Possible values are from 0 to `max_brightness`. 1302 | int brightness() const { return get_attr_int("brightness"); } 1303 | led set_brightness(int v) { 1304 | set_attr_int("brightness", v); 1305 | return *this; 1306 | } 1307 | 1308 | // Triggers: read-only 1309 | // Returns a list of available triggers. 1310 | mode_set triggers() const { return get_attr_set("trigger"); } 1311 | 1312 | // Trigger: read/write 1313 | // Sets the led trigger. A trigger 1314 | // is a kernel based source of led events. Triggers can either be simple or 1315 | // complex. A simple trigger isn't configurable and is designed to slot into 1316 | // existing subsystems with minimal additional code. Examples are the `ide-disk` and 1317 | // `nand-disk` triggers. 1318 | // 1319 | // Complex triggers whilst available to all LEDs have LED specific 1320 | // parameters and work on a per LED basis. The `timer` trigger is an example. 1321 | // The `timer` trigger will periodically change the LED brightness between 1322 | // 0 and the current brightness setting. The `on` and `off` time can 1323 | // be specified via `delay_{on,off}` attributes in milliseconds. 1324 | // You can change the brightness value of a LED independently of the timer 1325 | // trigger. However, if you set the brightness value to 0 it will 1326 | // also disable the `timer` trigger. 1327 | std::string trigger() const { return get_attr_from_set("trigger"); } 1328 | led set_trigger(std::string v) { 1329 | set_attr_string("trigger", v); 1330 | return *this; 1331 | } 1332 | 1333 | // Delay On: read/write 1334 | // The `timer` trigger will periodically change the LED brightness between 1335 | // 0 and the current brightness setting. The `on` time can 1336 | // be specified via `delay_on` attribute in milliseconds. 1337 | int delay_on() const { return get_attr_int("delay_on"); } 1338 | led set_delay_on(int v) { 1339 | set_attr_int("delay_on", v); 1340 | return *this; 1341 | } 1342 | 1343 | // Delay Off: read/write 1344 | // The `timer` trigger will periodically change the LED brightness between 1345 | // 0 and the current brightness setting. The `off` time can 1346 | // be specified via `delay_off` attribute in milliseconds. 1347 | int delay_off() const { return get_attr_int("delay_off"); } 1348 | led set_delay_off(int v) { 1349 | set_attr_int("delay_off", v); 1350 | return *this; 1351 | } 1352 | 1353 | 1354 | // Gets the LED's brightness as a percentage (0-1) of the maximum. 1355 | float brightness_pct() const { 1356 | return static_cast(brightness()) / max_brightness(); 1357 | } 1358 | 1359 | // Sets the LED's brightness as a percentage (0-1) of the maximum. 1360 | led set_brightness_pct(float v) { 1361 | return set_brightness(v * max_brightness()); 1362 | } 1363 | 1364 | // Turns the led on by setting its brightness to the maximum level. 1365 | void on() { set_brightness(max_brightness()); } 1366 | 1367 | // Turns the led off. 1368 | void off() { set_brightness(0); } 1369 | 1370 | // Enables timer trigger and sets delay_on and delay_off attributes to the 1371 | // provided values (in milliseconds). 1372 | void flash(unsigned on_ms, unsigned off_ms); 1373 | 1374 | #if defined(EV3DEV_PLATFORM_BRICKPI) 1375 | static led blue_led1; 1376 | static led blue_led2; 1377 | 1378 | static std::vector led1; 1379 | static std::vector led2; 1380 | 1381 | static std::vector black; 1382 | static std::vector blue; 1383 | #elif defined(EV3DEV_PLATFORM_BRICKPI3) 1384 | 1385 | static led amber_led1; 1386 | 1387 | static std::vector led1; 1388 | 1389 | static std::vector black; 1390 | static std::vector blue; 1391 | 1392 | #elif defined(EV3DEV_PLATFORM_PISTORMS) 1393 | static led red_left; 1394 | static led red_right; 1395 | static led green_left; 1396 | static led green_right; 1397 | static led blue_left; 1398 | static led blue_right; 1399 | 1400 | static std::vector left; 1401 | static std::vector right; 1402 | 1403 | static std::vector black; 1404 | static std::vector red; 1405 | static std::vector green; 1406 | static std::vector blue; 1407 | static std::vector yellow; 1408 | static std::vector purple; 1409 | static std::vector cyan; 1410 | static std::vector white; 1411 | static std::vector orange; 1412 | #else 1413 | static led red_left; 1414 | static led red_right; 1415 | static led green_left; 1416 | static led green_right; 1417 | 1418 | static std::vector left; 1419 | static std::vector right; 1420 | 1421 | static std::vector black; 1422 | static std::vector red; 1423 | static std::vector green; 1424 | static std::vector amber; 1425 | static std::vector orange; 1426 | static std::vector yellow; 1427 | #endif 1428 | 1429 | // Assigns to each led in `group` corresponding brightness percentage from `color`. 1430 | static void set_color(const std::vector &group, const std::vector &color); 1431 | 1432 | static void all_off(); 1433 | 1434 | protected: 1435 | int _max_brightness = 0; 1436 | }; 1437 | 1438 | //----------------------------------------------------------------------------- 1439 | // A generic interface to read data from the system's power_supply class. 1440 | // Uses the built-in legoev3-battery if none is specified. 1441 | //----------------------------------------------------------------------------- 1442 | class power_supply : protected device { 1443 | public: 1444 | power_supply(std::string name); 1445 | 1446 | using device::connected; 1447 | 1448 | // Measured Current: read-only 1449 | // The measured current that the battery is supplying (in microamps) 1450 | int measured_current() const { return get_attr_int("current_now"); } 1451 | 1452 | // Measured Voltage: read-only 1453 | // The measured voltage that the battery is supplying (in microvolts) 1454 | int measured_voltage() const { return get_attr_int("voltage_now"); } 1455 | 1456 | // Max Voltage: read-only 1457 | int max_voltage() const { return get_attr_int("voltage_max_design"); } 1458 | 1459 | // Min Voltage: read-only 1460 | int min_voltage() const { return get_attr_int("voltage_min_design"); } 1461 | 1462 | // Technology: read-only 1463 | std::string technology() const { return get_attr_string("technology"); } 1464 | 1465 | // Type: read-only 1466 | std::string type() const { return get_attr_string("type"); } 1467 | 1468 | float measured_amps() const { return measured_current() / 1000000.f; } 1469 | float measured_volts() const { return measured_voltage() / 1000000.f; } 1470 | 1471 | static power_supply battery; 1472 | }; 1473 | 1474 | //----------------------------------------------------------------------------- 1475 | // EV3 buttons 1476 | //----------------------------------------------------------------------------- 1477 | class button { 1478 | public: 1479 | button(int bit); 1480 | 1481 | // Check if the button is pressed. 1482 | bool pressed() const; 1483 | 1484 | // Gets called whenever the button state changes. 1485 | // The user has to call the process() function to check for state change. 1486 | std::function onclick; 1487 | 1488 | // Check if the button state has changed, 1489 | // call onclick function in case it has. 1490 | // Returns true if the state has changed since the last call. 1491 | bool process(); 1492 | 1493 | static button back; 1494 | static button left; 1495 | static button right; 1496 | static button up; 1497 | static button down; 1498 | static button enter; 1499 | 1500 | // Call process() for each of the EV3 buttons. 1501 | // Returns true if any of the states have changed since the last call. 1502 | static bool process_all(); 1503 | 1504 | private: 1505 | int _bit; 1506 | bool _state = false; 1507 | std::vector _buf; 1508 | 1509 | struct file_descriptor { 1510 | int _fd; 1511 | 1512 | file_descriptor(const char *path, int flags); 1513 | ~file_descriptor(); 1514 | operator int() { return _fd; } 1515 | }; 1516 | 1517 | std::shared_ptr _fd; 1518 | }; 1519 | 1520 | //----------------------------------------------------------------------------- 1521 | // EV3 Sound 1522 | //----------------------------------------------------------------------------- 1523 | class sound { 1524 | public: 1525 | static void beep(const std::string &args = "", bool bSynchronous = false); 1526 | static void tone(float frequency, float ms, bool bSynchronous = false); 1527 | static void tone(const std::vector< std::vector > &sequence, bool bSynchronous = false); 1528 | static void play(const std::string &soundfile, bool bSynchronous = false); 1529 | static void speak(const std::string &text, bool bSynchronous = false); 1530 | }; 1531 | 1532 | //----------------------------------------------------------------------------- 1533 | // EV3 LCD 1534 | //----------------------------------------------------------------------------- 1535 | class lcd { 1536 | public: 1537 | lcd(); 1538 | ~lcd(); 1539 | 1540 | bool available() const { return _fb != nullptr; } 1541 | 1542 | uint32_t resolution_x() const { return _xres; } 1543 | uint32_t resolution_y() const { return _yres; } 1544 | uint32_t bits_per_pixel() const { return _bpp; } 1545 | 1546 | uint32_t frame_buffer_size() const { return _fbsize; } 1547 | uint32_t line_length() const { return _llength; } 1548 | 1549 | unsigned char *frame_buffer() { return _fb; } 1550 | 1551 | void fill(unsigned char pixel); 1552 | 1553 | protected: 1554 | void init(); 1555 | void deinit(); 1556 | 1557 | private: 1558 | unsigned char *_fb; 1559 | uint32_t _fbsize; 1560 | uint32_t _llength; 1561 | uint32_t _xres; 1562 | uint32_t _yres; 1563 | uint32_t _bpp; 1564 | }; 1565 | 1566 | //----------------------------------------------------------------------------- 1567 | // EV3 remote control 1568 | //----------------------------------------------------------------------------- 1569 | class remote_control { 1570 | public: 1571 | remote_control(unsigned channel = 1); 1572 | remote_control(infrared_sensor&, unsigned channel = 1); 1573 | virtual ~remote_control(); 1574 | 1575 | inline bool connected() const { return _sensor->connected(); } 1576 | inline unsigned channel() const { return _channel+1; } 1577 | 1578 | bool process(); 1579 | 1580 | std::function on_red_up; 1581 | std::function on_red_down; 1582 | std::function on_blue_up; 1583 | std::function on_blue_down; 1584 | std::function on_beacon; 1585 | std::function on_state_change; 1586 | 1587 | enum buttons 1588 | { 1589 | red_up = (1 << 0), 1590 | red_down = (1 << 1), 1591 | blue_up = (1 << 2), 1592 | blue_down = (1 << 3), 1593 | beacon = (1 << 4), 1594 | }; 1595 | 1596 | protected: 1597 | virtual void on_value_changed(int value); 1598 | 1599 | infrared_sensor *_sensor = nullptr; 1600 | bool _owns_sensor = false; 1601 | unsigned _channel = 0; 1602 | int _value = 0; 1603 | int _state = 0; 1604 | }; 1605 | 1606 | //----------------------------------------------------------------------------- 1607 | // The `lego-port` class provides an interface for working with input and 1608 | // output ports that are compatible with LEGO MINDSTORMS RCX/NXT/EV3, LEGO 1609 | // WeDo and LEGO Power Functions sensors and motors. Supported devices include 1610 | // the LEGO MINDSTORMS EV3 Intelligent Brick, the LEGO WeDo USB hub and 1611 | // various sensor multiplexers from 3rd party manufacturers. 1612 | // 1613 | // Some types of ports may have multiple modes of operation. For example, the 1614 | // input ports on the EV3 brick can communicate with sensors using UART, I2C 1615 | // or analog validate signals - but not all at the same time. Therefore there 1616 | // are multiple modes available to connect to the different types of sensors. 1617 | // 1618 | // In most cases, ports are able to automatically detect what type of sensor 1619 | // or motor is connected. In some cases though, this must be manually specified 1620 | // using the `mode` and `set_device` attributes. The `mode` attribute affects 1621 | // how the port communicates with the connected device. For example the input 1622 | // ports on the EV3 brick can communicate using UART, I2C or analog voltages, 1623 | // but not all at the same time, so the mode must be set to the one that is 1624 | // appropriate for the connected sensor. The `set_device` attribute is used to 1625 | // specify the exact type of sensor that is connected. Note: the mode must be 1626 | // correctly set before setting the sensor type. 1627 | // 1628 | // Ports can be found at `/sys/class/lego-port/port` where `` is 1629 | // incremented each time a new port is registered. Note: The number is not 1630 | // related to the actual port at all - use the `address` attribute to find 1631 | // a specific port. 1632 | //----------------------------------------------------------------------------- 1633 | class lego_port : protected device { 1634 | public: 1635 | lego_port(address_type); 1636 | 1637 | using device::connected; 1638 | using device::device_index; 1639 | 1640 | // Address: read-only 1641 | // Returns the name of the port. See individual driver documentation for 1642 | // the name that will be returned. 1643 | std::string address() const { return get_attr_string("address"); } 1644 | 1645 | // Driver Name: read-only 1646 | // Returns the name of the driver that loaded this device. You can find the 1647 | // complete list of drivers in the [list of port drivers]. 1648 | std::string driver_name() const { return get_attr_string("driver_name"); } 1649 | 1650 | // Modes: read-only 1651 | // Returns a list of the available modes of the port. 1652 | mode_set modes() const { return get_attr_set("modes"); } 1653 | 1654 | // Mode: read/write 1655 | // Reading returns the currently selected mode. Writing sets the mode. 1656 | // Generally speaking when the mode changes any sensor or motor devices 1657 | // associated with the port will be removed new ones loaded, however this 1658 | // this will depend on the individual driver implementing this class. 1659 | std::string mode() const { return get_attr_string("mode"); } 1660 | lego_port set_mode(std::string v) { 1661 | set_attr_string("mode", v); 1662 | return *this; 1663 | } 1664 | 1665 | // Set Device: write-only 1666 | // For modes that support it, writing the name of a driver will cause a new 1667 | // device to be registered for that driver and attached to this port. For 1668 | // example, since NXT/Analog sensors cannot be auto-detected, you must use 1669 | // this attribute to load the correct driver. Returns -EOPNOTSUPP if setting a 1670 | // device is not supported. 1671 | lego_port set_set_device(std::string v) { 1672 | set_attr_string("set_device", v); 1673 | return *this; 1674 | } 1675 | 1676 | // Status: read-only 1677 | // In most cases, reading status will return the same value as `mode`. In 1678 | // cases where there is an `auto` mode additional values may be returned, 1679 | // such as `no-device` or `error`. See individual port driver documentation 1680 | // for the full list of possible values. 1681 | std::string status() const { return get_attr_string("status"); } 1682 | 1683 | protected: 1684 | lego_port() {} 1685 | 1686 | bool connect(const std::map>&) noexcept; 1687 | }; 1688 | 1689 | } // namespace ev3dev 1690 | -------------------------------------------------------------------------------- /templates/autogen-header.liquid: -------------------------------------------------------------------------------- 1 | 2 | // Sections of the following code were auto-generated based on spec v{{ meta.version }}{% if meta.specRevision %}, rev {{meta.specRevision}}{% endif %}. 3 | -------------------------------------------------------------------------------- /templates/generic-class-description.liquid: -------------------------------------------------------------------------------- 1 | {% for line in currentClass.description %} 2 | // {{ line }}{% 3 | endfor %} 4 | -------------------------------------------------------------------------------- /templates/generic-declare-property-value.liquid: -------------------------------------------------------------------------------- 1 | {% for prop in currentClass.propertyValues %}{% 2 | assign prefix = prop.propertyName | downcase | underscore_spaces %}{% 3 | for value in prop.values %}{% 4 | for line in value.description %} 5 | // {{ line }}{% 6 | endfor %}{% 7 | assign cppName = value.name | downcase | underscore_non_wc %} 8 | static constexpr char {{ prefix }}_{{cppName}}[] = "{{ value.name }}"; 9 | {% endfor %}{% 10 | endfor %} 11 | -------------------------------------------------------------------------------- /templates/generic-define-property-value.liquid: -------------------------------------------------------------------------------- 1 | {% for prop in currentClass.propertyValues %}{% 2 | assign className = currentClass.friendlyName | downcase | underscore_spaces %}{% 3 | assign prefix = prop.propertyName | downcase | underscore_spaces %}{% 4 | for value in prop.values %}{% 5 | assign cppName = value.name | downcase | underscore_non_wc %} 6 | constexpr char {{className}}::{{ prefix }}_{{cppName}}[];{% 7 | endfor %}{% 8 | endfor %} 9 | -------------------------------------------------------------------------------- /templates/generic-get-set.liquid: -------------------------------------------------------------------------------- 1 | {% for prop in currentClass.systemProperties %} 2 | // {{ prop.name }}:{% 3 | if prop.readAccess %}{% 4 | if prop.writeAccess %} read/write{% 5 | else %} read-only{% 6 | endif %}{% 7 | else %} write-only{% 8 | endif %}{% 9 | for line in prop.description %} 10 | // {{ line }}{% 11 | endfor %}{% 12 | assign cppName = prop.name | downcase | underscore_spaces %}{% 13 | assign type = prop.type %}{% 14 | assign getter = prop.type %}{% 15 | assign setter = prop.type %}{% 16 | if prop.type == 'string' %}{% 17 | assign type = 'std::string' %}{% 18 | elsif prop.type == 'string array' %}{% 19 | assign type = 'mode_set' %}{% 20 | assign getter = 'set' %}{% 21 | elsif prop.type == 'string selector' %}{% 22 | assign type = 'std::string' %}{% 23 | assign getter = 'from_set' %}{% 24 | assign setter = 'string' %}{% 25 | endif %}{% 26 | if prop.readAccess == true %} 27 | {{ type }} {{ cppName }}() const { return get_attr_{{ getter }}("{{ prop.systemName }}"); }{% 28 | endif %}{% 29 | if prop.writeAccess == true %} 30 | auto set_{{ cppName }}({{ type }} v) -> decltype(*this) { 31 | set_attr_{{ setter }}("{{ prop.systemName }}", v); 32 | return *this; 33 | }{% 34 | endif %} 35 | {%endfor %} 36 | -------------------------------------------------------------------------------- /templates/generic_report_status.liquid: -------------------------------------------------------------------------------- 1 | {% for prop in currentClass.systemProperties %}{% 2 | assign cppName = prop.name | downcase | underscore_spaces %}{% 3 | if prop.readAccess == true %} 4 | cout << " {{ prop.name }}: "; 5 | try { cout << dev.{{ cppName }}() << endl; } 6 | catch(...) { cout << "[" << strerror(errno) << "]" << endl; }{% 7 | endif %}{% 8 | endfor %} 9 | 10 | -------------------------------------------------------------------------------- /templates/leds-declare.liquid: -------------------------------------------------------------------------------- 1 | {% for instance in currentClass.instances %}{% 2 | assign instanceName = instance.name | downcase | underscore_spaces %} 3 | static led {{instanceName}};{% 4 | endfor %} 5 | {% for group in currentClass.groups %}{% 6 | assign groupName = group.name | downcase | underscore_spaces %} 7 | static std::vector {{ groupName }};{% 8 | endfor %} 9 | {% for color in currentClass.colors %}{% 10 | assign colorName = color.name | downcase | underscore_spaces %} 11 | static std::vector {{ colorName }};{% 12 | endfor %} 13 | -------------------------------------------------------------------------------- /templates/leds-define.liquid: -------------------------------------------------------------------------------- 1 | {% for instance in currentClass.instances %}{% 2 | assign instanceName = instance.name | downcase | underscore_spaces %} 3 | led led::{{instanceName}}{"{{instance.systemName}}"};{% 4 | endfor %} 5 | {% for group in currentClass.groups %}{% 6 | assign groupName = group.name | downcase | underscore_spaces %}{% 7 | assign ledNames = '' %}{% 8 | for name in group.entries %}{% 9 | capture ledNames %}{{ ledNames }}&led::{{ name | downcase | underscore_spaces }}{% unless forloop.last %}, {% endunless %}{% 10 | endcapture %}{% 11 | endfor %} 12 | std::vector led::{{ groupName }}{ {{ ledNames }} };{% 13 | endfor %} 14 | {% for color in currentClass.colors %}{% 15 | assign colorName = color.name | downcase | underscore_spaces %}{% 16 | assign mixValues = '' %}{% 17 | for value in color.value %}{% 18 | capture mixValues %}{{ mixValues }}static_cast({{ value }}){% unless forloop.last %}, {% endunless %}{% 19 | endcapture %}{% 20 | endfor %} 21 | std::vector led::{{ colorName }}{ {{ mixValues }} };{% 22 | endfor %} 23 | 24 | //----------------------------------------------------------------------------- 25 | void led::all_off() { 26 | {% for instance in currentClass.instances %}{% 27 | assign instanceName = instance.name | downcase | underscore_spaces %} 28 | {{instanceName}}.off();{% 29 | endfor %} 30 | 31 | } 32 | -------------------------------------------------------------------------------- /templates/motor_commands.liquid: -------------------------------------------------------------------------------- 1 | {% for prop in currentClass.propertyValues %}{% 2 | if prop.propertyName == 'Command' %}{% 3 | assign className = currentClass.friendlyName | downcase | underscore_spaces %}{% 4 | for value in prop.values %}{% 5 | assign commandName = value.name | downcase | underscore_non_wc %}{% 6 | if commandName == 'float' %}{% 7 | assign commandName = 'float_' %}{% 8 | endif %}{% 9 | for line in value.description %} 10 | // {{ line }}{% 11 | endfor%} 12 | void {{ commandName }}() { set_command("{{ value.name }}"); } 13 | {% endfor %}{% 14 | endif %}{% 15 | endfor %} 16 | -------------------------------------------------------------------------------- /templates/special-sensor-declaration.liquid: -------------------------------------------------------------------------------- 1 | {% 2 | assign className = currentClass.friendlyName | downcase | underscore_spaces %}{% 3 | assign parentName = currentClass.inheritance | downcase | underscore_spaces %}{% 4 | for line in currentClass.description %} 5 | // {{ line }}{% 6 | endfor %} 7 | class {{ className }} : public {{ parentName }} 8 | { 9 | public: 10 | {{className}}(address_type address = INPUT_AUTO); 11 | {% for prop in currentClass.propertyValues %}{% 12 | assign prefix = prop.propertyName | downcase | underscore_spaces %}{% 13 | for value in prop.values %}{% 14 | for line in value.description %} 15 | // {{ line }}{% 16 | endfor %}{% 17 | assign cppName = value.name | downcase | underscore_non_wc %} 18 | static constexpr char {{ prefix }}_{{cppName}}[] = "{{ value.name }}"; 19 | {% endfor %}{% 20 | endfor %} 21 | {% for mapping in currentClass.sensorValueMappings %}{% 22 | assign name = mapping.name | downcase | underscore_spaces %}{% 23 | for line in mapping.description %} 24 | // {{ line }}{% 25 | endfor %}{% 26 | assign mode = mapping.requiredMode | downcase | underscore_non_wc %}{% 27 | assign num_values = mapping.type | size %}{% 28 | if num_values == 1 %}{% 29 | assign type = mapping.type[0] %}{% 30 | assign reader = 'value' %}{% 31 | if type == 'float' %}{% 32 | assign reader = 'float_value' %}{% 33 | elsif type == 'boolean' %}{% 34 | assign type = 'bool' %}{% 35 | endif %} 36 | {{ type }} {{ name }}(bool do_set_mode = true) { 37 | if (do_set_mode) set_mode(mode_{{ mode }}); 38 | return {{ reader }}({{ mapping.sourceValue[0] }}); 39 | } 40 | {% else %} 41 | std::tuple<{% 42 | for cur_type in mapping.type %}{% 43 | assign type = cur_type %}{% 44 | if type == 'boolean' %}{% 45 | assign type = 'bool' %}{% 46 | endif %}{{ type }}{% 47 | unless forloop.last %}, {% endunless %}{% 48 | endfor %}> {{ name }}(bool do_set_mode = true) { 49 | if (do_set_mode) set_mode(mode_{{ mode }}); 50 | return std::make_tuple( {% 51 | for value_index in mapping.sourceValue %}{% 52 | assign reader = 'value' %}{% 53 | if mapping.type[forloop.index] == 'float' %}{% 54 | assign reader = 'float_value' %}{% 55 | endif %}{{ reader }}({{ value_index }}){% 56 | unless forloop.last %}, {% endunless %}{% 57 | endfor %} ); 58 | } 59 | {% endif %}{% 60 | endfor %} 61 | }; 62 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(api_tests 2 | api_tests.cpp 3 | ${CMAKE_CURRENT_SOURCE_DIR}/../ev3dev.cpp 4 | ) 5 | 6 | target_include_directories(api_tests PRIVATE 7 | ${CMAKE_CURRENT_SOURCE_DIR}/.. 8 | ) 9 | 10 | target_compile_options(api_tests PRIVATE -std=c++0x) 11 | 12 | target_compile_definitions(api_tests PRIVATE 13 | SYS_ROOT="${CMAKE_CURRENT_SOURCE_DIR}/fake-sys/arena" 14 | FAKE_SYS="${CMAKE_CURRENT_SOURCE_DIR}/fake-sys" 15 | ) 16 | 17 | add_test(api_tests api_tests) 18 | -------------------------------------------------------------------------------- /tests/api_tests.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include "catch.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace ev3 = ev3dev; 9 | 10 | void populate_arena(const std::vector &devices) { 11 | std::ostringstream command; 12 | command << FAKE_SYS "/populate_arena.py"; 13 | for (auto d : devices) command << " " << d; 14 | 15 | system(FAKE_SYS "/clean_arena.py"); 16 | system(command.str().c_str()); 17 | } 18 | 19 | TEST_CASE( "Device" ) { 20 | populate_arena({"medium_motor:0@ev3-ports:outA", "infrared_sensor:0@ev3-ports:in1"}); 21 | 22 | ev3::device d; 23 | 24 | SECTION("connect any motor") { 25 | d.connect(SYS_ROOT "/tacho-motor/", "motor", {}); 26 | REQUIRE(d.connected()); 27 | } 28 | 29 | SECTION("connect specific motor") { 30 | d.connect(SYS_ROOT "/tacho-motor/", "motor0", {}); 31 | REQUIRE(d.connected()); 32 | } 33 | 34 | SECTION("connect a motor by driver name") { 35 | d.connect(SYS_ROOT "/tacho-motor/", "motor", 36 | {{std::string("driver_name"), {std::string("lego-ev3-m-motor")}}}); 37 | REQUIRE(d.connected()); 38 | } 39 | 40 | SECTION("connect a motor by address") { 41 | d.connect(SYS_ROOT "/tacho-motor/", "motor", 42 | {{std::string("address"), {ev3::OUTPUT_A}}}); 43 | REQUIRE(d.connected()); 44 | } 45 | 46 | SECTION("invalid driver name") { 47 | d.connect(SYS_ROOT "/tacho-motor/", "motor", 48 | {{std::string("driver_name"), {std::string("not-valid")}}}); 49 | REQUIRE(!d.connected()); 50 | } 51 | 52 | SECTION("connect a sensor") { 53 | d.connect(SYS_ROOT "/lego-sensor/", "sensor", {}); 54 | REQUIRE(d.connected()); 55 | } 56 | } 57 | 58 | TEST_CASE("Medium Motor") { 59 | populate_arena({"medium_motor:0@ev3-ports:outA"}); 60 | 61 | ev3::medium_motor m; 62 | 63 | REQUIRE(m.connected()); 64 | REQUIRE(m.device_index() == 0); 65 | 66 | SECTION("reading same attribute twice") { 67 | REQUIRE(m.driver_name() == "lego-ev3-m-motor"); 68 | REQUIRE(m.driver_name() == "lego-ev3-m-motor"); 69 | } 70 | 71 | SECTION("check attribute values") { 72 | std::set commands = { 73 | "run-forever", "run-to-abs-pos", "run-to-rel-pos", "run-timed", 74 | "run-direct", "stop", "reset" 75 | }; 76 | 77 | std::set state = {"running"}; 78 | 79 | REQUIRE(m.count_per_rot() == 360); 80 | REQUIRE(m.commands() == commands); 81 | REQUIRE(m.duty_cycle() == 0); 82 | REQUIRE(m.duty_cycle_sp() == 42); 83 | REQUIRE(m.polarity() == "normal"); 84 | REQUIRE(m.address() == "ev3-ports:outA"); 85 | REQUIRE(m.position() == 42); 86 | REQUIRE(m.position_sp() == 42); 87 | REQUIRE(m.ramp_down_sp() == 0); 88 | REQUIRE(m.ramp_up_sp() == 0); 89 | REQUIRE(m.speed() == 0); 90 | REQUIRE(m.speed_sp() == 0); 91 | REQUIRE(m.state() == state); 92 | REQUIRE(m.stop_action() == "coast"); 93 | REQUIRE(m.time_sp() == 1000); 94 | } 95 | } 96 | 97 | TEST_CASE("Infrared Sensor") { 98 | populate_arena({"infrared_sensor:0@ev3-ports:in1"}); 99 | ev3::infrared_sensor s; 100 | 101 | REQUIRE(s.connected()); 102 | 103 | REQUIRE(s.device_index() == 0); 104 | REQUIRE(s.bin_data_format() == "s8"); 105 | REQUIRE(s.num_values() == 1); 106 | REQUIRE(s.address() == "ev3-ports:in1"); 107 | REQUIRE(s.value(0) == 16); 108 | 109 | std::vector v(1); 110 | s.bin_data(v.data()); 111 | 112 | REQUIRE(v[0] == 16); 113 | REQUIRE(s.bin_data() == v); 114 | } 115 | --------------------------------------------------------------------------------