510 likes | 524 Views
Learn how to develop software for Miniature Smart Vehicles using C++, CMake, Git, Makefile, and more. Includes step-by-step instructions, code examples, and unit testing.
E N D
DIT 168 Industrial IT and Embedded Systems “Miniature Smart Vehicles”
Software Development with C++ CMake Git Makefile Source make Compiler Binary Tests Runner
Software Development with C++ • Preparing your Ubuntu 16.04 LTS: sudo apt-get update sudo apt-get upgrade sudo apt-get install cmake build-essential • You might also need: sudo apt-get install git
Software Development with C++ CMake Git Makefile Source make Compiler Binary Tests Runner • We start with HelloWorld: #include <iostream> int main(intargc, char** argv) { std::cout << "Hello World!" << std::endl; return 0; } • Compiling: g++ -o helloworld helloworld.cpp
Software Development with C++ CMake Git Makefile Source make Compiler Binary Tests Runner • We add a CMakeLists.txt: cmake_minimum_required(VERSION 3.2) project(helloworld) add_executable(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/helloworld.cpp) • Compiling: mkdir build cd build cmake .. make
Software Development with C++ CMake Git Makefile Source make Compiler Binary Tests Runner Making the program more useful: #include <cstdint> #include <iostream> boolisPrime(uint16_t n) { boolretVal{true}; if (n<2 || 0 == n%2) { retVal = false; } else { for(uint16_t i{3}; (i*i) <= n; i += 2) { if (0 == n%i) { returnfalse; break; } } } returnretVal; } intmain(intargc, char** argv) { std::cout << "HelloWorld = " << isPrime(43) << std::endl; return 0; }
Software Development with C++ CMake Git Makefile Source make Compiler Binary Tests Runner • Now, we need to change CMakeLists.txt to support uint16_t from a newer C++ standard: cmake_minimum_required(VERSION 3.2) project(helloworld) set(CMAKE_CXX_STANDARD 11) add_executable(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/helloworld.cpp) • Compiling: mkdir build cd build cmake .. make
Software Development with C++ #include "PrimeChecker.hpp" boolPrimeChecker::isPrime(uint16_t n) { boolretVal{true}; if (n<2 || 0 == n%2) { retVal = false; } else { for(uint16_t i{3}; (i*i) <= n; i += 2) { if (0 == n%i) { returnfalse; break; } } } returnretVal; } • Let’s add unit tests: wgethttps://github.com/catchorg/Catch2/releases/download/v2.1.1/catch.hpp • We need to refactor our program for testability: #ifndef PRIMECHECKER #define PRIMECHECKER #include <cstdint> classPrimeChecker { public: boolisPrime(uint16_t n); }; #endif
Software Development with C++ • #include <iostream> • #include "PrimeChecker.hpp" • intmain(intargc, char** argv) { • PrimeCheckerpc; • std::cout << "Hello World = " << pc.isPrime(43) << std::endl; • return 0; • } • cmake_minimum_required(VERSION 3.2) • project(helloworld) • set(CMAKE_CXX_STANDARD 11) • add_executable(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/helloworld.cpp${CMAKE_CURRENT_SOURCE_DIR}/PrimeChecker.cpp) • Let’s add unit tests: wgethttps://github.com/catchorg/Catch2/releases/download/v2.1.1/catch.hpp • We need to refactor our program for testability:
Software Development with C++ CMake Git Makefile Source make Compiler Binary Tests Runner • Now, we can add a test case: #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() – only do this once per test-runner #include "catch.hpp" #include "PrimeChecker.hpp" TEST_CASE("Test PrimeChecker 1.") { PrimeChecker pc; REQUIRE(pc.isPrime(5)); } • Compiling: g++ -std=c++11 -o runner TestPrimeChecker.cppPrimeChecker.cpp
Software Development with C++ CMake Git Makefile Source make Compiler Binary Tests Runner Let’s , add more test cases: #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() – only do this once per test-runner #include "catch.hpp" #include "PrimeChecker.hpp" TEST_CASE("Test PrimeChecker 1.") { PrimeChecker pc; REQUIRE(pc.isPrime(5)); } TEST_CASE("Test PrimeChecker 2.") { PrimeChecker pc; REQUIRE(!pc.isPrime(2)); } TEST_CASE("Test PrimeChecker 3.") { PrimeChecker pc; REQUIRE(pc.isPrime(3)); } TEST_CASE("Test PrimeChecker 4.") { PrimeChecker pc; REQUIRE(pc.isPrime(9)); }
Software Development with C++ CMake Git Makefile Source make Compiler Binary Tests Runner • Compiling manually: g++ -std=c++11 -o runner TestPrimeChecker.cppPrimeChecker.cpp • Adjusting our CMakeLists.txt: cmake_minimum_required(VERSION 3.2) project(helloworld) set(CMAKE_CXX_STANDARD 11) add_executable(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/helloworld.cpp ${CMAKE_CURRENT_SOURCE_DIR}/PrimeChecker.cpp) enable_testing() add_executable(${PROJECT_NAME}-Runner TestPrimeChecker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/PrimeChecker.cpp) add_test(NAME ${PROJECT_NAME}-Runner COMMAND ${PROJECT_NAME}-Runner)
Software Development with C++ CMake Git Makefile Source make Compiler Binary Tests Runner • Let’s improve the quality of the program: cmake_minimum_required(VERSION 3.2) project(helloworld) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror") add_executable(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/helloworld.cpp ${CMAKE_CURRENT_SOURCE_DIR}/PrimeChecker.cpp) enable_testing() add_executable(${PROJECT_NAME}-Runner TestPrimeChecker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/PrimeChecker.cpp) add_test(NAME ${PROJECT_NAME}-Runner COMMAND ${PROJECT_NAME}-Runner)
Software Development with C++ CMake Git Makefile Source make Compiler Binary Tests Runner • Now, we need to fix our program: #include <iostream> #include "PrimeChecker.hpp" intmain(int /*argc*/, char** /*argv*/) { PrimeCheckerpc; std::cout << "Hello World = " << pc.isPrime(43) << std::endl; return 0; }
Software Development with C++ • Let’s add communication to our program: sudo add-apt-repository ppa:chrberger/libcluon sudo apt-get update sudo apt-get install libcluon • Adjusting our CMakeLists.txt to find libcluon: cmake_minimum_required(VERSION 3.2) project(helloworld) set(CMAKE_CXX_STANDARD 11) find_package(libcluon REQUIRED) include_directories(SYSTEM ${CLUON_INCLUDE_DIRS}) add_executable(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/helloworld.cpp ${CMAKE_CURRENT_SOURCE_DIR}/PrimeChecker.cpp) target_link_libraries(${PROJECT_NAME} ${CLUON_LIBRARIES}) enable_testing() add_executable(${PROJECT_NAME}-Runner TestPrimeChecker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/PrimeChecker.cpp) target_link_libraries(${PROJECT_NAME}-Runner ${CLUON_LIBRARIES}) add_test(NAME ${PROJECT_NAME}-Runner COMMAND ${PROJECT_NAME}-Runner)
Software Development with C++ CMake Git Makefile Source make Compiler Binary Tests Runner • Let’s add communication (sending data): #include <iostream> #include "cluon/UDPSender.hpp" #include "PrimeChecker.hpp" intmain(int /*argc*/, char** /*argv*/) { PrimeCheckerpc; std::cout << "Hello World = " << pc.isPrime(43) << std::endl; cluon::UDPSendersender{"127.0.0.1", 1234}; sender.send("Hello World!"); return 0; } • Test communication: nc -l -u 1234 ./helloworld
Software Development with C++ • Let’s add communication (receiving data): #include <chrono> #include <iostream> #include "cluon/UDPSender.hpp" #include "cluon/UDPReceiver.hpp" #include "PrimeChecker.hpp" intmain(int /*argc*/, char** /*argv*/) { PrimeCheckerpc; std::cout << "Hello World = " << pc.isPrime(43) << std::endl; cluon::UDPSendersender{"127.0.0.1", 1234}; sender.send("Hello World!"); cluon::UDPReceiverreceiver("0.0.0.0", 1235, [](std::string &&data, std::string &&/*from*/, std::chrono::system_clock::time_point &&/*timepoint*/) noexcept { std::cout << "Received " << data.size() << " bytes." << std::endl; }); usingnamespacestd::literals::chrono_literals; while (receiver.isRunning()) { std::this_thread::sleep_for(1s); } return 0; }
Software Development with C++ • Adjusting our CMakeLists.txt: cmake_minimum_required(VERSION 3.2) project(helloworld) set(CMAKE_CXX_STANDARD 14) find_package(libcluon REQUIRED) include_directories(SYSTEM ${CLUON_INCLUDE_DIRS}) add_executable(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/helloworld.cpp ${CMAKE_CURRENT_SOURCE_DIR}/PrimeChecker.cpp) target_link_libraries(${PROJECT_NAME} ${CLUON_LIBRARIES}) enable_testing() add_executable(${PROJECT_NAME}-Runner TestPrimeChecker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/PrimeChecker.cpp) target_link_libraries(${PROJECT_NAME}-Runner ${CLUON_LIBRARIES}) add_test(NAME ${PROJECT_NAME}-Runner COMMAND ${PROJECT_NAME}-Runner) • Test communication: echo "Hi there!" | nc -u 127.0.0.1 1235
Software Development with C++ • Install test program: sudoapt-getinstallsocat • Let’s communicate via multicast (1-to-many): #include <chrono> #include <iostream> #include "cluon/UDPSender.hpp" #include "cluon/UDPReceiver.hpp" #include "PrimeChecker.hpp" intmain(int /*argc*/, char** /*argv*/) { PrimeCheckerpc; std::cout << "Hello World = " << pc.isPrime(43) << std::endl; cluon::UDPSendersender{"225.0.0.111", 1236}; sender.send("Hello World!"); usingnamespacestd::literals::chrono_literals; std::this_thread::sleep_for(5s); cluon::UDPReceiverreceiver("225.0.0.111", 1236, [](std::string &&data, std::string &&/*from*/, std::chrono::system_clock::time_point &&/*timepoint*/) noexcept { std::cout << "Received " << data.size() << " bytes." << std::endl; }); usingnamespacestd::literals::chrono_literals; while (receiver.isRunning()) { std::this_thread::sleep_for(1s); } return 0; }
Software Development with C++ • Test communication (receiving from our program): socat UDP4-RECVFROM:1236,ip-add-membership=225.0.0.111:0.0.0.0,fork - • Test communication (sending to our program): echo "Hi there" | nc -u 225.0.0.111 1236 • Let’s test on a real network: • Connect to AP –MiniSmartVehicles on your host • Change network adapter for VirtualBox to “Bridged Network" • Test pinging a known host ping 192.168.1.102 • Run your program (…andwaitforsomeonesendingsomedata): ./helloworld
Software Development with C++ • Let’s add structure to the communication: • Add a message specification message MyTestMessage1 [id = 2001] { uint16 myValue [id = 1]; } • Create C++ bindings (manual example) cluon-msc --cpp-headers --out=messages.hppmessages.odvd cluon-msc --cpp-sources --cpp-add-include-file=messages.hpp --out=messages.cppmessages.odvd
Software Development with C++ • Let’s add structure to the communication: cmake_minimum_required(VERSION 3.2) project(helloworld) set(CMAKE_CXX_STANDARD 14) find_package(libcluon REQUIRED) include_directories(SYSTEM ${CLUON_INCLUDE_DIRS}) add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/messages.cpp WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND cluon-msc --cpp-sources --cpp-add-include-file=messages.hpp --out=${CMAKE_BINARY_DIR}/messages.cpp ${CMAKE_CURRENT_SOURCE_DIR}/messages.odvd COMMAND cluon-msc --cpp-headers --out=${CMAKE_BINARY_DIR}/messages.hpp ${CMAKE_CURRENT_SOURCE_DIR}/messages.odvd DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/messages.odvd) include_directories(SYSTEM ${CMAKE_BINARY_DIR}) add_executable(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/helloworld.cpp ${CMAKE_CURRENT_SOURCE_DIR}/PrimeChecker.cpp ${CMAKE_BINARY_DIR}/messages.cpp) target_link_libraries(${PROJECT_NAME} ${CLUON_LIBRARIES}) enable_testing() add_executable(${PROJECT_NAME}-Runner TestPrimeChecker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/PrimeChecker.cpp ${CMAKE_BINARY_DIR}/messages.cpp) target_link_libraries(${PROJECT_NAME}-Runner ${CLUON_LIBRARIES}) add_test(NAME ${PROJECT_NAME}-Runner COMMAND ${PROJECT_NAME}-Runner)
Software Development with C++ • Let’s add structure to the communication: #include <chrono> #include <iostream> #include "cluon/UDPSender.hpp" #include "cluon/UDPReceiver.hpp" #include "cluon/ToProtoVisitor.hpp" #include "cluon/FromProtoVisitor.hpp" #include "PrimeChecker.hpp“ #include "messages.hpp" intmain(int /*argc*/, char** /*argv*/) { PrimeCheckerpc; std::cout << "Hello World = " << pc.isPrime(43) << std::endl; cluon::UDPSendersender{"225.0.0.111", 1238}; uint16_t value; std::cout << "Enter a numberto check: "; std::cin >> value; MyTestMessage1 msg; msg.myValue(value); cluon::ToProtoVisitorencoder; msg.accept(encoder); std::stringdata{encoder.encodedData()}; sender.send(std::move(data)); usingnamespacestd::literals::chrono_literals; std::this_thread::sleep_for(5s); cluon::UDPReceiverreceiver("225.0.0.111", 1238, [](std::string &&data, std::string &&/*from*/, std::chrono::system_clock::time_point &&/*timepoint*/) noexcept { std::stringstreamsstr{data}; cluon::FromProtoVisitordecoder; decoder.decodeFrom(sstr); MyTestMessage1 receivedMsg; receivedMsg.accept(decoder); PrimeCheckerpc; std::cout << receivedMsg.myValue() << " is " << (pc.isPrime(receivedMsg.myValue()) ? "" : "not") << " a prime." << std::endl; }); usingnamespacestd::literals::chrono_literals; while (receiver.isRunning()) { std::this_thread::sleep_for(1s); } return 0; }
Software Development with C++ • Details about message specification: message MyTestMessage1 [id = 2001] { ... } message MyTestMessage2 [id = 2002] { bool myValue1 [id = 1]; uint8 myValue2 [id = 2]; int8 myValue3 [id = 3]; uint16 myValue4 [id = 4]; int16 myValue5 [id = 5]; uint32 myValue6 [id = 6]; int32 myValue7 [id = 7]; uint64 myValue8 [id = 8]; int64 myValue9 [id = 9]; float myValue10 [id = 10]; double myValue11 [id = 11]; string myValue12 [id = 12]; MyTestMessage1 myValue13 [id = 13]; }
Software Development with C++ • How to handle multiple messages? #include <cstdint> #include <chrono> #include <iostream> #include <sstream> #include "cluon/OD4Session.hpp" #include "cluon/Envelope.hpp" #include "PrimeChecker.hpp" #include "messages.hpp" intmain(int /*argc*/, char** /*argv*/) { PrimeCheckerpc; std::cout << "Hello World = " << pc.isPrime(43) << std::endl; cluon::OD4Session od4(111, [](cluon::data::Envelope &&envelope) noexcept { if (envelope.dataType() == 2001) { MyTestMessage1 receivedMsg = cluon::extractMessage<MyTestMessage1>(std::move(envelope)); PrimeCheckerpc; std::cout << receivedMsg.myValue() << " is " << (pc.isPrime(receivedMsg.myValue()) ? "" : "not") << " a prime." << std::endl; } }); uint16_t value; std::cout << "Enter a numberto check: "; std::cin >> value; MyTestMessage1 msg; msg.myValue(value); od4.send(msg); return 0; }
Software Development with C++ • Installing Docker (https://docs.docker.com/install/linux/docker-ce/ubuntu/#install-using-the-repository): curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" sudo apt-get update sudo apt-get install docker-ce sudogroupadddocker sudousermod -aGdocker $USER groups • Logout and login docker run hello-world
Software Development with C++ • Manually building with Docker: docker run --rm -ti -v $PWD:/opt/sources ubuntu:16.04 /bin/bash / # • Now, cmake fails we need to install all dependencies into our Docker container: apt-get update apt-get install build-essential cmake apt-get install software-properties-common add-apt-repository ppa:chrberger/libcluon apt-get update apt-get install libcluon • Manually building with Docker: cd /opt/sources mkdir build && cd build cmake .. make && make test ./helloworld
Software Development with C++ • Manually building with Docker: docker run --rm -ti--net=host -v $PWD:/opt/sources alpine:3.7 /bin/sh / # • Now, cmake fails again we need to install all dependencies into our Docker container: apk update apk --no-cache add ca-certificates cmake g++ make apk add libcluon --no-cache --repository https://chrberger.github.io/libcluon/alpine/v3.7 --allow-untrusted • Manually building with Docker: cd /opt/sources mkdir build && cd build cmake .. make && make test ./helloworld
Software Development with C++ • Making the changes persistent and automated – create a Dockerfile: FROM alpine:3.7 as builder MAINTAINER Christian Berger christian.berger@gu.se RUN apk update && \ apk --no-cache add \ ca-certificates \ cmake \ g++ \ make && \ apk add libcluon --no-cache --repository https://chrberger.github.io/libcluon/alpine/v3.7 --allow-untrusted ADD . /opt/sources WORKDIR /opt/sources RUN cd /opt/sources && \ mkdir build && \ cd build && \ cmake -D CMAKE_BUILD_TYPE=Release .. && \ make && make test && cphelloworld /tmp # Deploy. FROM alpine:3.7 MAINTAINER Christian Berger christian.berger@gu.se RUN apk update && \ apk add libcluon --no-cache --repository https://chrberger.github.io/libcluon/alpine/v3.7 --allow-untrusted && \ mkdir /opt WORKDIR /opt COPY --from=builder /tmp/helloworld . CMD ["/opt/helloworld"]
Software Development with C++ • Running and testing the build: docker build -t myrepository/mydockerimage . docker run --rm-ti --net=host myrepository/mydockerimage • Saving a Docker image: dockersave myrepository/mydockerimage > myImage.tar • Loading a Docker image (after transferring to the car): cat myImage.tar | dockerload • Alternative: use Docker Hub: https://docs.docker.com/docker-hub/
Software Development with C++ native compiler: g++ cross-compiler: arm-linux-gnueabihf-g++ emulated native compiler: g++ qemu-arm arm x86_64 x86_64 Creating code for armhf platforms • Compile directly on target platform • Running cross-compiler on host to generate code for target platform • Emulating target-platform on host to generate code
Software Development with C++ • Building for armhf– new Dockerfile.armhf: FROM pipill/armhf-alpine:edge as builder MAINTAINER Christian Berger christian.berger@gu.se RUN [ "cross-build-start" ] RUN cat /etc/apk/repositories && \ echo http://dl-4.alpinelinux.org/alpine/v3.7/main > /etc/apk/repositories && \ echo http://dl-4.alpinelinux.org/alpine/v3.7/community >> /etc/apk/repositories RUN apk update && \ apk --no-cache add \ ca-certificates \ cmake \ g++ \ make && \ apk add libcluon --no-cache --repository https://chrberger.github.io/libcluon/alpine/v3.7 --allow-untrusted ADD . /opt/sources WORKDIR /opt/sources RUN cd /opt/sources && \ mkdir build && \ cd build && \ cmake -D CMAKE_BUILD_TYPE=Release .. && \ make && make test && cphelloworld /tmp RUN [ "cross-build-end" ] # Deploy. FROM pipill/armhf-alpine:edge MAINTAINER Christian Berger christian.berger@gu.se RUN [ "cross-build-start" ] RUN cat /etc/apk/repositories && \ echo http://dl-4.alpinelinux.org/alpine/v3.7/main > /etc/apk/repositories && \ echo http://dl-4.alpinelinux.org/alpine/v3.7/community >> /etc/apk/repositories RUN apk update && \ apk add libcluon --no-cache --repository https://chrberger.github.io/libcluon/alpine/v3.7 --allow-untrusted && \ mkdir /opt WORKDIR /opt COPY --from=builder /tmp/helloworld . RUN [ "cross-build-end" ] CMD ["/opt/helloworld"]
Interfacing with the Vehicle OpenDaVINCI/OpenDLV Software Stack PS4Controller
Interfacing with the Vehicle OpenDaVINCI/OpenDLV Software Stack Motor msg opendlv-proxy-miniature-pwm-motor opendlv-proxy-miniature-ps4controller msg Steering Servo
Interfacing with the Vehicle • Docker image with the proxies: seresearch/2018-dit-168:v0.3.5-armhf • Microservices to run from OpenDaVINCI/OpenDLV software stack: • configuration file: global.buffer.memorySegmentSize = 2800000 global.buffer.numberOfMemorySegments = 4 odsupercomponent.pulsetimeack.timeout = 5000 # (in milliseconds) odsupercomponent.pulsetimeack.exclude = odcockpit odcockpit.directoriesForSharedLibaries = /opt/opendlv.miniature proxy-miniature-ps4controller.ps4controllerdevicenode = /dev/input/js0 proxy-miniature-ps4controller.deceleration.max = -1.0 proxy-miniature-ps4controller.acceleration.max = 0.25 proxy-miniature-ps4controller.steering.max = 38.0 proxy-miniature-pwm-motor.names = steering,propulsion proxy-miniature-pwm-motor.types = servo,esc proxy-miniature-pwm-motor.channels = 1,2
Interfacing with the Vehicle • Docker image with the proxies: seresearch/2018-dit-168:v0.3.5-armhf • Microservices to run from OpenDaVINCI/OpenDLV software stack: • configurationfile: global.buffer.memorySegmentSize = 2800000 global.buffer.numberOfMemorySegments = 4 odsupercomponent.pulsetimeack.timeout = 5000 # (in milliseconds) odsupercomponent.pulsetimeack.exclude = odcockpit odcockpit.directoriesForSharedLibaries = /opt/opendlv.miniature proxy-miniature-ps4controller.ps4controllerdevicenode = /dev/input/js0 proxy-miniature-ps4controller.deceleration.max = -1.0 proxy-miniature-ps4controller.acceleration.max = 0.25 proxy-miniature-ps4controller.steering.max = 38.0 proxy-miniature-pwm-motor.names = steering,propulsion proxy-miniature-pwm-motor.types = servo,esc proxy-miniature-pwm-motor.channels = 1,2
Interfacing with the Vehicle • Docker image with the proxies: seresearch/2018-dit-168:v0.3.5-armhf • Microservices to run from OpenDaVINCI/OpenDLV software stack for interfacing the vehicle with PS4 controller: • odsupercomponent + previous configuration file docker run --rm -ti --net=host -v $PWD:/opt/miniature.dataseresearch/2018-dit-168:v0.3.5-armhf /opt/od4/bin/odsupercomponent --cid=111 --verbose=1 --configuration=/opt/miniature.data/configuration • opendlv-proxy-miniature-pwm-motor docker run --rm -ti --net=host --privileged=true -v /sys:/sys -v /dev:/devseresearch/2018-dit-168:v0.3.5-armhf /opt/opendlv.miniature/bin/opendlv-proxy-miniature-pwm-motor --cid=111 --freq=50 --id=1 • opendlv-proxy-miniature-ps4controller docker run --rm -ti --net=host -v /dev/input/js0:/dev/input/js0seresearch/2018-dit-168:v0.3.5-armhf /opt/opendlv.miniature/bin/opendlv-proxy-miniature-ps4controller --cid=111 --freq=10
Interfacing with the Vehicle with Your SW libcluon-based microservices OpenDaVINCI/OpenDLV Software Stack Motor msg <Your Software> <Your Software> opendlv-proxy-miniature-pwm-motor <Your Software> msg Steering Servo
Interfacing with the Vehicle with Your SW libcluon-based microservices OpenDaVINCI/OpenDLV Software Stack Motor msg <Your Software> <Your Software> opendlv-proxy-miniature-pwm-motor <Your Software> msg Steering Servo message opendlv.proxy.GroundSteeringReading [id = 1090] { float steeringAngle [id = 1]; } message opendlv.proxy.PedalPositionReading [id = 1041] { float percent [id = 1]; }
Interfacing with the Vehicle with Your SW • Handling several microservices manually error-prone • Use docker-compose • Install on Ubuntu 16.04 LTS (cf. https://docs.docker.com/compose/install/) • sudo curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose • sudochmod +x /usr/local/bin/docker-compose • Test: docker-compose --version
Interfacing with the Vehicle with Your SW • docker-compose.yml: version: '2' services: odsupercomponent: image: seresearch/2018-dit-168:v0.3.5-armhf network_mode: "host" restart: always volumes: - .:/opt/miniature.data command: "/opt/od4/bin/odsupercomponent --cid=111 --verbose=1 --configuration=/opt/miniature.data/configuration" proxy-miniature-pwm-motor: image: seresearch/2018-dit-168:v0.3.5-armhf depends_on: - odsupercomponent network_mode: "host" restart: always privileged: true volumes: - /sys/:/sys/ - /dev/:/dev/ command: "/opt/opendlv.miniature/bin/opendlv-proxy-miniature-pwm-motor --cid=111 --freq=50 --id=1" # Your microservice. proxy-miniature-ps4controller: image: YourARMHFImage network_mode: "host" restart: always command: "YourCommand"
Viewing Exchanged Messages Simple web-based message viewer: • Docker image (x86_64): docker run --rm -ti --net=host -p 8080:8080 chrberger/dit168-signal-viewer:v0.0.1 --cid=111 • GitHub repository: https://github.com/chrberger/dit168-signal-viewer • If you add or change messages, simply modify messages.odvd and rebuild the message viewer.
To-Do List • Power ON • Connect your laptop to your car • Software preparation • Install, update, docker pull, git clone … • “Teach” it to run
Connect to You Car Wi-Fi hotspot: BeagleBone-xxxx Password: BeagleBone On your Ubuntu: $ ping 192.168.8.1 $ ssh debian@192.168.8.1 (Default password is “temppwd”) $ rc_blink $ sudo /opt/scripts/tools/grow_partition.sh $ df -h
Connect to Internet If you are using a router / Wi-Fi hotspot other than Eduroam $ connmanctl > scan wifi > services > agent on > connect wifi_[the_long_wifi_ID] > exit $ ping 8.8.8.8 When typing the long wifi ID (not the SSID), you can type first several characters and press tab for auto-complete.
Connect to the Internet Solution provided by : Oleks <oleks@oleks.info> https://github.com/oleks/eduroam-wpa_supplicant • Configuration for eduroam (not yet confirmed) $ sudonano /etc/wpa_supplicant/wpa_supplicant.conf ctrl_interface_group=root ap_scan=1 eapol_version=2 update_config=0 network={ disabled=0 auth_alg=OPEN ssid="eduroam” scan_ssid=1 key_mgmt=WPA-EAP proto=WPA RSN pairwise=CCMP TKIP eap=PEAP identity="abc123@ku.dk" # Edit this, anonymous_identity="anonymous@ku.dk" # this, password=hash:36cae0f7deee765c0a46693591d10801 # and this. phase1="peaplabel=0” phase2="auth=MSCHAPV2” }
Connect to the Internet Solution provided by : Oleks <oleks@oleks.info> https://github.com/oleks/eduroam-wpa_supplicant • How to cover your password $ echo -n 'password' | iconv -t utf16le | openssl md4 • How to get it work $ sudowpa_supplicant -Dwext -iwlan0 -c wpa_supplicant.conf -B (Use "$ lspci -k" and "$ ifconfig -a" to check kernel driver stated and the network interface name for yourwirelesscard) $ ping 8.8.8.8
Software Preparation • apt-get update / upgrade • Install libcluon and docker • Pull the docker image "seresearch/2018-dit-168:v0.3.5-armhf" • Transfer files from your Ubuntu to the car: $ scp [file_name] debian@192.168.8.1:[target_path] "$ man scp" for details of this command • Clone the gitHub repository "https://github.com/Adam2092/DIT168Example"
Before "let it go"… • Compile and Cross-compile • Run the docker image for odsupercomponent and proxy • Unplug the charger and turn on the ESC before running • In case no network connection: configure local loopback device to allow UDP multicast sessions $ sudoifconfig lo multicast $ -sudo route add -net 224.0.0.0 netmask 240.0.0.0 dev lo • Runyourcode / docker image now • Runthemindividually: Slide 37 • Runthemaltogether: Slide 41 (installdocker-composefirst) $ docker-compose -up