From 2b7fe87df2e1d31eb37e2956d523f0e281311be1 Mon Sep 17 00:00:00 2001 From: drechsler Date: Sun, 1 Nov 2020 21:34:18 +0100 Subject: [PATCH 01/12] moved files for new repo structure --- {docker => docker.old}/.gitignore | 0 {docker => docker.old}/Makefile | 0 {docker => docker.old}/README.md | 0 {docker => docker.old}/idf/Dockerfile | 0 {docker => docker.old}/idf/idf_tools.py.patch | 0 {docker => docker.old}/platformio/Dockerfile | 0 {docker => docker.old}/qemu/Dockerfile | 0 {docker => docker.old}/vscode/Dockerfile | 0 {doc => docs}/CODEOWNERS | 0 {doc => docs}/Makefile | 0 {doc => docs}/esp32_overview.gv | 0 {doc => docs}/esp32_overview.svg | 0 ..._Time_Kernel-A_Hands-On_Tutorial_Guide.pdf | Bin .../FreeRTOS_Reference_Manual_V10.0.0.pdf | Bin .../source-code-for-book-examples.zip | Bin .../.template}/CMakeLists.txt | 0 .../esp32-components/.template}/Kconfig | 0 .../esp32-components/.template}/README.md | 0 .../.template}/include/counter.h | 0 .../esp32-components/.template}/src/counter.c | 0 .../.template}/test/CMakeLists.txt | 0 .../esp32-components/.template}/test/test.c | 0 .../esp32-components}/README.md | 0 .../esp32-components}/channel/CMakeLists.txt | 0 .../esp32-components}/channel/Kconfig | 0 .../esp32-components}/channel/README.md | 0 .../channel/include/channel.h | 0 .../channel/include/channel_debug.h | 0 .../channel/include/channel_internal.h | 0 .../esp32-components}/channel/src/channel.c | 0 .../channel/test/CMakeLists.txt | 0 .../channel/test/channel_test.c | 0 .../lifesensor_common/CMakeLists.txt | 0 .../lifesensor_common/README.md | 0 .../lifesensor_common/include/macro/queue.h | 0 .../lifesensor_common/include/macro/task.h | 0 .../esp32-components}/linux/CMakeLists.txt | 0 .../esp32-components}/linux/include/list.h | 0 .../esp32-components}/spo2/CMakeLists.txt | 0 .../esp32-components}/spo2/README.md | 0 .../esp32-components}/spo2/include/spo2.h | 0 .../spo2/include/spo2_driver.h | 0 .../spo2/include/spo2_filter.h | 0 .../esp32-components}/spo2/src/spo2.c | 0 .../esp32-components}/spo2/src/spo2_driver.c | 0 .../esp32-components}/spo2/src/spo2_filter.c | 0 .../esp32-components}/ulp_adc/CMakeLists.txt | 0 .../esp32-components}/ulp_adc/README.md | 0 .../esp32-components}/ulp_adc/component.mk | 0 .../esp32-components}/ulp_adc/include/ulp.h | 0 .../esp32-components}/ulp_adc/src/ulp.c | 0 .../ulp_adc/ulp/ulp_program.S | 0 .../esp32-firmware/prototype}/.gitignore | 0 .../esp32-firmware/prototype}/CMakeLists.txt | 0 .../esp32-firmware/prototype}/Makefile | 0 .../esp32-firmware/prototype}/README.md | 0 .../prototype}/main/CMakeLists.txt | 0 .../esp32-firmware/prototype}/main/main.c | 0 .../prototype}/sdkconfig.defaults | 0 .../esp32-firmware/qemu}/.gitignore | 0 .../esp32-firmware/qemu}/CMakeLists.txt | 0 .../esp32-firmware/qemu}/Makefile | 0 .../esp32-firmware/qemu}/README.md | 0 .../esp32-firmware/qemu}/main/CMakeLists.txt | 0 .../esp32-firmware/qemu}/main/component.mk | 0 .../esp32-firmware/qemu}/main/main.c | 0 .../esp32-firmware/qemu}/sdkconfig.defaults | 0 .../esp32-firmware/test}/.gitignore | 0 .../esp32-firmware/test}/CMakeLists.txt | 0 .../esp32-firmware/test}/Makefile | 0 .../esp32-firmware/test}/README.md | 0 .../esp32-firmware/test}/main/CMakeLists.txt | 0 .../esp32-firmware/test}/main/component.mk | 0 .../esp32-firmware/test}/main/main.c | 0 .../esp32-firmware/test}/sdkconfig.defaults | 0 products/prototype/README.md | 40 ++++++++++++++++++ products/prototype/v1/Makefile | 5 +++ README.md => products/prototype/v1/README.md | 0 products/prototype/v1/docs/README.md | 12 ++++++ products/prototype/v1/requirements/README.md | 17 ++++++++ 80 files changed, 74 insertions(+) rename {docker => docker.old}/.gitignore (100%) rename {docker => docker.old}/Makefile (100%) rename {docker => docker.old}/README.md (100%) rename {docker => docker.old}/idf/Dockerfile (100%) rename {docker => docker.old}/idf/idf_tools.py.patch (100%) rename {docker => docker.old}/platformio/Dockerfile (100%) rename {docker => docker.old}/qemu/Dockerfile (100%) rename {docker => docker.old}/vscode/Dockerfile (100%) rename {doc => docs}/CODEOWNERS (100%) rename {doc => docs}/Makefile (100%) rename {doc => docs}/esp32_overview.gv (100%) rename {doc => docs}/esp32_overview.svg (100%) rename {doc => docs}/freertos/161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf (100%) rename {doc => docs}/freertos/FreeRTOS_Reference_Manual_V10.0.0.pdf (100%) rename {doc => docs}/freertos/source-code-for-book-examples.zip (100%) rename {components/template => parts/esp32-components/.template}/CMakeLists.txt (100%) rename {components/template => parts/esp32-components/.template}/Kconfig (100%) rename {components/template => parts/esp32-components/.template}/README.md (100%) rename {components/template => parts/esp32-components/.template}/include/counter.h (100%) rename {components/template => parts/esp32-components/.template}/src/counter.c (100%) rename {components/template => parts/esp32-components/.template}/test/CMakeLists.txt (100%) rename {components/template => parts/esp32-components/.template}/test/test.c (100%) rename {components => parts/esp32-components}/README.md (100%) rename {components => parts/esp32-components}/channel/CMakeLists.txt (100%) rename {components => parts/esp32-components}/channel/Kconfig (100%) rename {components => parts/esp32-components}/channel/README.md (100%) rename {components => parts/esp32-components}/channel/include/channel.h (100%) rename {components => parts/esp32-components}/channel/include/channel_debug.h (100%) rename {components => parts/esp32-components}/channel/include/channel_internal.h (100%) rename {components => parts/esp32-components}/channel/src/channel.c (100%) rename {components => parts/esp32-components}/channel/test/CMakeLists.txt (100%) rename {components => parts/esp32-components}/channel/test/channel_test.c (100%) rename {components => parts/esp32-components}/lifesensor_common/CMakeLists.txt (100%) rename {components => parts/esp32-components}/lifesensor_common/README.md (100%) rename {components => parts/esp32-components}/lifesensor_common/include/macro/queue.h (100%) rename {components => parts/esp32-components}/lifesensor_common/include/macro/task.h (100%) rename {components => parts/esp32-components}/linux/CMakeLists.txt (100%) rename {components => parts/esp32-components}/linux/include/list.h (100%) rename {components => parts/esp32-components}/spo2/CMakeLists.txt (100%) rename {components => parts/esp32-components}/spo2/README.md (100%) rename {components => parts/esp32-components}/spo2/include/spo2.h (100%) rename {components => parts/esp32-components}/spo2/include/spo2_driver.h (100%) rename {components => parts/esp32-components}/spo2/include/spo2_filter.h (100%) rename {components => parts/esp32-components}/spo2/src/spo2.c (100%) rename {components => parts/esp32-components}/spo2/src/spo2_driver.c (100%) rename {components => parts/esp32-components}/spo2/src/spo2_filter.c (100%) rename {components => parts/esp32-components}/ulp_adc/CMakeLists.txt (100%) rename {components => parts/esp32-components}/ulp_adc/README.md (100%) rename {components => parts/esp32-components}/ulp_adc/component.mk (100%) rename {components => parts/esp32-components}/ulp_adc/include/ulp.h (100%) rename {components => parts/esp32-components}/ulp_adc/src/ulp.c (100%) rename {components => parts/esp32-components}/ulp_adc/ulp/ulp_program.S (100%) rename {lifesensor => parts/esp32-firmware/prototype}/.gitignore (100%) rename {lifesensor => parts/esp32-firmware/prototype}/CMakeLists.txt (100%) rename {lifesensor => parts/esp32-firmware/prototype}/Makefile (100%) rename {lifesensor => parts/esp32-firmware/prototype}/README.md (100%) rename {lifesensor => parts/esp32-firmware/prototype}/main/CMakeLists.txt (100%) rename {lifesensor => parts/esp32-firmware/prototype}/main/main.c (100%) rename {lifesensor => parts/esp32-firmware/prototype}/sdkconfig.defaults (100%) rename {lifesensor_ci => parts/esp32-firmware/qemu}/.gitignore (100%) rename {lifesensor_ci => parts/esp32-firmware/qemu}/CMakeLists.txt (100%) rename {lifesensor_ci => parts/esp32-firmware/qemu}/Makefile (100%) rename {lifesensor_ci => parts/esp32-firmware/qemu}/README.md (100%) rename {lifesensor_ci => parts/esp32-firmware/qemu}/main/CMakeLists.txt (100%) rename {lifesensor_ci => parts/esp32-firmware/qemu}/main/component.mk (100%) rename {lifesensor_ci => parts/esp32-firmware/qemu}/main/main.c (100%) rename {lifesensor_ci => parts/esp32-firmware/qemu}/sdkconfig.defaults (100%) rename {lifesensor_test => parts/esp32-firmware/test}/.gitignore (100%) rename {lifesensor_test => parts/esp32-firmware/test}/CMakeLists.txt (100%) rename {lifesensor_test => parts/esp32-firmware/test}/Makefile (100%) rename {lifesensor_test => parts/esp32-firmware/test}/README.md (100%) rename {lifesensor_test => parts/esp32-firmware/test}/main/CMakeLists.txt (100%) rename {lifesensor_test => parts/esp32-firmware/test}/main/component.mk (100%) rename {lifesensor_test => parts/esp32-firmware/test}/main/main.c (100%) rename {lifesensor_test => parts/esp32-firmware/test}/sdkconfig.defaults (100%) create mode 100644 products/prototype/README.md create mode 100644 products/prototype/v1/Makefile rename README.md => products/prototype/v1/README.md (100%) create mode 100644 products/prototype/v1/docs/README.md create mode 100644 products/prototype/v1/requirements/README.md diff --git a/docker/.gitignore b/docker.old/.gitignore similarity index 100% rename from docker/.gitignore rename to docker.old/.gitignore diff --git a/docker/Makefile b/docker.old/Makefile similarity index 100% rename from docker/Makefile rename to docker.old/Makefile diff --git a/docker/README.md b/docker.old/README.md similarity index 100% rename from docker/README.md rename to docker.old/README.md diff --git a/docker/idf/Dockerfile b/docker.old/idf/Dockerfile similarity index 100% rename from docker/idf/Dockerfile rename to docker.old/idf/Dockerfile diff --git a/docker/idf/idf_tools.py.patch b/docker.old/idf/idf_tools.py.patch similarity index 100% rename from docker/idf/idf_tools.py.patch rename to docker.old/idf/idf_tools.py.patch diff --git a/docker/platformio/Dockerfile b/docker.old/platformio/Dockerfile similarity index 100% rename from docker/platformio/Dockerfile rename to docker.old/platformio/Dockerfile diff --git a/docker/qemu/Dockerfile b/docker.old/qemu/Dockerfile similarity index 100% rename from docker/qemu/Dockerfile rename to docker.old/qemu/Dockerfile diff --git a/docker/vscode/Dockerfile b/docker.old/vscode/Dockerfile similarity index 100% rename from docker/vscode/Dockerfile rename to docker.old/vscode/Dockerfile diff --git a/doc/CODEOWNERS b/docs/CODEOWNERS similarity index 100% rename from doc/CODEOWNERS rename to docs/CODEOWNERS diff --git a/doc/Makefile b/docs/Makefile similarity index 100% rename from doc/Makefile rename to docs/Makefile diff --git a/doc/esp32_overview.gv b/docs/esp32_overview.gv similarity index 100% rename from doc/esp32_overview.gv rename to docs/esp32_overview.gv diff --git a/doc/esp32_overview.svg b/docs/esp32_overview.svg similarity index 100% rename from doc/esp32_overview.svg rename to docs/esp32_overview.svg diff --git a/doc/freertos/161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf b/docs/freertos/161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf similarity index 100% rename from doc/freertos/161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf rename to docs/freertos/161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf diff --git a/doc/freertos/FreeRTOS_Reference_Manual_V10.0.0.pdf b/docs/freertos/FreeRTOS_Reference_Manual_V10.0.0.pdf similarity index 100% rename from doc/freertos/FreeRTOS_Reference_Manual_V10.0.0.pdf rename to docs/freertos/FreeRTOS_Reference_Manual_V10.0.0.pdf diff --git a/doc/freertos/source-code-for-book-examples.zip b/docs/freertos/source-code-for-book-examples.zip similarity index 100% rename from doc/freertos/source-code-for-book-examples.zip rename to docs/freertos/source-code-for-book-examples.zip diff --git a/components/template/CMakeLists.txt b/parts/esp32-components/.template/CMakeLists.txt similarity index 100% rename from components/template/CMakeLists.txt rename to parts/esp32-components/.template/CMakeLists.txt diff --git a/components/template/Kconfig b/parts/esp32-components/.template/Kconfig similarity index 100% rename from components/template/Kconfig rename to parts/esp32-components/.template/Kconfig diff --git a/components/template/README.md b/parts/esp32-components/.template/README.md similarity index 100% rename from components/template/README.md rename to parts/esp32-components/.template/README.md diff --git a/components/template/include/counter.h b/parts/esp32-components/.template/include/counter.h similarity index 100% rename from components/template/include/counter.h rename to parts/esp32-components/.template/include/counter.h diff --git a/components/template/src/counter.c b/parts/esp32-components/.template/src/counter.c similarity index 100% rename from components/template/src/counter.c rename to parts/esp32-components/.template/src/counter.c diff --git a/components/template/test/CMakeLists.txt b/parts/esp32-components/.template/test/CMakeLists.txt similarity index 100% rename from components/template/test/CMakeLists.txt rename to parts/esp32-components/.template/test/CMakeLists.txt diff --git a/components/template/test/test.c b/parts/esp32-components/.template/test/test.c similarity index 100% rename from components/template/test/test.c rename to parts/esp32-components/.template/test/test.c diff --git a/components/README.md b/parts/esp32-components/README.md similarity index 100% rename from components/README.md rename to parts/esp32-components/README.md diff --git a/components/channel/CMakeLists.txt b/parts/esp32-components/channel/CMakeLists.txt similarity index 100% rename from components/channel/CMakeLists.txt rename to parts/esp32-components/channel/CMakeLists.txt diff --git a/components/channel/Kconfig b/parts/esp32-components/channel/Kconfig similarity index 100% rename from components/channel/Kconfig rename to parts/esp32-components/channel/Kconfig diff --git a/components/channel/README.md b/parts/esp32-components/channel/README.md similarity index 100% rename from components/channel/README.md rename to parts/esp32-components/channel/README.md diff --git a/components/channel/include/channel.h b/parts/esp32-components/channel/include/channel.h similarity index 100% rename from components/channel/include/channel.h rename to parts/esp32-components/channel/include/channel.h diff --git a/components/channel/include/channel_debug.h b/parts/esp32-components/channel/include/channel_debug.h similarity index 100% rename from components/channel/include/channel_debug.h rename to parts/esp32-components/channel/include/channel_debug.h diff --git a/components/channel/include/channel_internal.h b/parts/esp32-components/channel/include/channel_internal.h similarity index 100% rename from components/channel/include/channel_internal.h rename to parts/esp32-components/channel/include/channel_internal.h diff --git a/components/channel/src/channel.c b/parts/esp32-components/channel/src/channel.c similarity index 100% rename from components/channel/src/channel.c rename to parts/esp32-components/channel/src/channel.c diff --git a/components/channel/test/CMakeLists.txt b/parts/esp32-components/channel/test/CMakeLists.txt similarity index 100% rename from components/channel/test/CMakeLists.txt rename to parts/esp32-components/channel/test/CMakeLists.txt diff --git a/components/channel/test/channel_test.c b/parts/esp32-components/channel/test/channel_test.c similarity index 100% rename from components/channel/test/channel_test.c rename to parts/esp32-components/channel/test/channel_test.c diff --git a/components/lifesensor_common/CMakeLists.txt b/parts/esp32-components/lifesensor_common/CMakeLists.txt similarity index 100% rename from components/lifesensor_common/CMakeLists.txt rename to parts/esp32-components/lifesensor_common/CMakeLists.txt diff --git a/components/lifesensor_common/README.md b/parts/esp32-components/lifesensor_common/README.md similarity index 100% rename from components/lifesensor_common/README.md rename to parts/esp32-components/lifesensor_common/README.md diff --git a/components/lifesensor_common/include/macro/queue.h b/parts/esp32-components/lifesensor_common/include/macro/queue.h similarity index 100% rename from components/lifesensor_common/include/macro/queue.h rename to parts/esp32-components/lifesensor_common/include/macro/queue.h diff --git a/components/lifesensor_common/include/macro/task.h b/parts/esp32-components/lifesensor_common/include/macro/task.h similarity index 100% rename from components/lifesensor_common/include/macro/task.h rename to parts/esp32-components/lifesensor_common/include/macro/task.h diff --git a/components/linux/CMakeLists.txt b/parts/esp32-components/linux/CMakeLists.txt similarity index 100% rename from components/linux/CMakeLists.txt rename to parts/esp32-components/linux/CMakeLists.txt diff --git a/components/linux/include/list.h b/parts/esp32-components/linux/include/list.h similarity index 100% rename from components/linux/include/list.h rename to parts/esp32-components/linux/include/list.h diff --git a/components/spo2/CMakeLists.txt b/parts/esp32-components/spo2/CMakeLists.txt similarity index 100% rename from components/spo2/CMakeLists.txt rename to parts/esp32-components/spo2/CMakeLists.txt diff --git a/components/spo2/README.md b/parts/esp32-components/spo2/README.md similarity index 100% rename from components/spo2/README.md rename to parts/esp32-components/spo2/README.md diff --git a/components/spo2/include/spo2.h b/parts/esp32-components/spo2/include/spo2.h similarity index 100% rename from components/spo2/include/spo2.h rename to parts/esp32-components/spo2/include/spo2.h diff --git a/components/spo2/include/spo2_driver.h b/parts/esp32-components/spo2/include/spo2_driver.h similarity index 100% rename from components/spo2/include/spo2_driver.h rename to parts/esp32-components/spo2/include/spo2_driver.h diff --git a/components/spo2/include/spo2_filter.h b/parts/esp32-components/spo2/include/spo2_filter.h similarity index 100% rename from components/spo2/include/spo2_filter.h rename to parts/esp32-components/spo2/include/spo2_filter.h diff --git a/components/spo2/src/spo2.c b/parts/esp32-components/spo2/src/spo2.c similarity index 100% rename from components/spo2/src/spo2.c rename to parts/esp32-components/spo2/src/spo2.c diff --git a/components/spo2/src/spo2_driver.c b/parts/esp32-components/spo2/src/spo2_driver.c similarity index 100% rename from components/spo2/src/spo2_driver.c rename to parts/esp32-components/spo2/src/spo2_driver.c diff --git a/components/spo2/src/spo2_filter.c b/parts/esp32-components/spo2/src/spo2_filter.c similarity index 100% rename from components/spo2/src/spo2_filter.c rename to parts/esp32-components/spo2/src/spo2_filter.c diff --git a/components/ulp_adc/CMakeLists.txt b/parts/esp32-components/ulp_adc/CMakeLists.txt similarity index 100% rename from components/ulp_adc/CMakeLists.txt rename to parts/esp32-components/ulp_adc/CMakeLists.txt diff --git a/components/ulp_adc/README.md b/parts/esp32-components/ulp_adc/README.md similarity index 100% rename from components/ulp_adc/README.md rename to parts/esp32-components/ulp_adc/README.md diff --git a/components/ulp_adc/component.mk b/parts/esp32-components/ulp_adc/component.mk similarity index 100% rename from components/ulp_adc/component.mk rename to parts/esp32-components/ulp_adc/component.mk diff --git a/components/ulp_adc/include/ulp.h b/parts/esp32-components/ulp_adc/include/ulp.h similarity index 100% rename from components/ulp_adc/include/ulp.h rename to parts/esp32-components/ulp_adc/include/ulp.h diff --git a/components/ulp_adc/src/ulp.c b/parts/esp32-components/ulp_adc/src/ulp.c similarity index 100% rename from components/ulp_adc/src/ulp.c rename to parts/esp32-components/ulp_adc/src/ulp.c diff --git a/components/ulp_adc/ulp/ulp_program.S b/parts/esp32-components/ulp_adc/ulp/ulp_program.S similarity index 100% rename from components/ulp_adc/ulp/ulp_program.S rename to parts/esp32-components/ulp_adc/ulp/ulp_program.S diff --git a/lifesensor/.gitignore b/parts/esp32-firmware/prototype/.gitignore similarity index 100% rename from lifesensor/.gitignore rename to parts/esp32-firmware/prototype/.gitignore diff --git a/lifesensor/CMakeLists.txt b/parts/esp32-firmware/prototype/CMakeLists.txt similarity index 100% rename from lifesensor/CMakeLists.txt rename to parts/esp32-firmware/prototype/CMakeLists.txt diff --git a/lifesensor/Makefile b/parts/esp32-firmware/prototype/Makefile similarity index 100% rename from lifesensor/Makefile rename to parts/esp32-firmware/prototype/Makefile diff --git a/lifesensor/README.md b/parts/esp32-firmware/prototype/README.md similarity index 100% rename from lifesensor/README.md rename to parts/esp32-firmware/prototype/README.md diff --git a/lifesensor/main/CMakeLists.txt b/parts/esp32-firmware/prototype/main/CMakeLists.txt similarity index 100% rename from lifesensor/main/CMakeLists.txt rename to parts/esp32-firmware/prototype/main/CMakeLists.txt diff --git a/lifesensor/main/main.c b/parts/esp32-firmware/prototype/main/main.c similarity index 100% rename from lifesensor/main/main.c rename to parts/esp32-firmware/prototype/main/main.c diff --git a/lifesensor/sdkconfig.defaults b/parts/esp32-firmware/prototype/sdkconfig.defaults similarity index 100% rename from lifesensor/sdkconfig.defaults rename to parts/esp32-firmware/prototype/sdkconfig.defaults diff --git a/lifesensor_ci/.gitignore b/parts/esp32-firmware/qemu/.gitignore similarity index 100% rename from lifesensor_ci/.gitignore rename to parts/esp32-firmware/qemu/.gitignore diff --git a/lifesensor_ci/CMakeLists.txt b/parts/esp32-firmware/qemu/CMakeLists.txt similarity index 100% rename from lifesensor_ci/CMakeLists.txt rename to parts/esp32-firmware/qemu/CMakeLists.txt diff --git a/lifesensor_ci/Makefile b/parts/esp32-firmware/qemu/Makefile similarity index 100% rename from lifesensor_ci/Makefile rename to parts/esp32-firmware/qemu/Makefile diff --git a/lifesensor_ci/README.md b/parts/esp32-firmware/qemu/README.md similarity index 100% rename from lifesensor_ci/README.md rename to parts/esp32-firmware/qemu/README.md diff --git a/lifesensor_ci/main/CMakeLists.txt b/parts/esp32-firmware/qemu/main/CMakeLists.txt similarity index 100% rename from lifesensor_ci/main/CMakeLists.txt rename to parts/esp32-firmware/qemu/main/CMakeLists.txt diff --git a/lifesensor_ci/main/component.mk b/parts/esp32-firmware/qemu/main/component.mk similarity index 100% rename from lifesensor_ci/main/component.mk rename to parts/esp32-firmware/qemu/main/component.mk diff --git a/lifesensor_ci/main/main.c b/parts/esp32-firmware/qemu/main/main.c similarity index 100% rename from lifesensor_ci/main/main.c rename to parts/esp32-firmware/qemu/main/main.c diff --git a/lifesensor_ci/sdkconfig.defaults b/parts/esp32-firmware/qemu/sdkconfig.defaults similarity index 100% rename from lifesensor_ci/sdkconfig.defaults rename to parts/esp32-firmware/qemu/sdkconfig.defaults diff --git a/lifesensor_test/.gitignore b/parts/esp32-firmware/test/.gitignore similarity index 100% rename from lifesensor_test/.gitignore rename to parts/esp32-firmware/test/.gitignore diff --git a/lifesensor_test/CMakeLists.txt b/parts/esp32-firmware/test/CMakeLists.txt similarity index 100% rename from lifesensor_test/CMakeLists.txt rename to parts/esp32-firmware/test/CMakeLists.txt diff --git a/lifesensor_test/Makefile b/parts/esp32-firmware/test/Makefile similarity index 100% rename from lifesensor_test/Makefile rename to parts/esp32-firmware/test/Makefile diff --git a/lifesensor_test/README.md b/parts/esp32-firmware/test/README.md similarity index 100% rename from lifesensor_test/README.md rename to parts/esp32-firmware/test/README.md diff --git a/lifesensor_test/main/CMakeLists.txt b/parts/esp32-firmware/test/main/CMakeLists.txt similarity index 100% rename from lifesensor_test/main/CMakeLists.txt rename to parts/esp32-firmware/test/main/CMakeLists.txt diff --git a/lifesensor_test/main/component.mk b/parts/esp32-firmware/test/main/component.mk similarity index 100% rename from lifesensor_test/main/component.mk rename to parts/esp32-firmware/test/main/component.mk diff --git a/lifesensor_test/main/main.c b/parts/esp32-firmware/test/main/main.c similarity index 100% rename from lifesensor_test/main/main.c rename to parts/esp32-firmware/test/main/main.c diff --git a/lifesensor_test/sdkconfig.defaults b/parts/esp32-firmware/test/sdkconfig.defaults similarity index 100% rename from lifesensor_test/sdkconfig.defaults rename to parts/esp32-firmware/test/sdkconfig.defaults diff --git a/products/prototype/README.md b/products/prototype/README.md new file mode 100644 index 0000000..ec57556 --- /dev/null +++ b/products/prototype/README.md @@ -0,0 +1,40 @@ + +# Product template + + +This template should be used to create a new product. +1. Copy this template directory +2. Give your new product a meaningful name. +3. Append your new product to the product list in [../README.md](../README.md) +4. Edit this README + - Change title! +5. Continue editing in [v1/README.md](./v1/README.md) + +--- +## Product features + +- provides template for product + +--- +## Product versions + + + +- [v1/](./v1/) + - initial version + +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build & test all versions +- `make build` + - build all versions +- `make test` + - test all versions +- `make clean` + - clean all versions +- `make distclean` + - distclean all version \ No newline at end of file diff --git a/products/prototype/v1/Makefile b/products/prototype/v1/Makefile new file mode 100644 index 0000000..99bd0d1 --- /dev/null +++ b/products/prototype/v1/Makefile @@ -0,0 +1,5 @@ +### PARTS OF THE PRODUCT ### +PARTS += #empty +PARTS += interconnect/v1 + +include $(shell git rev-parse --show-toplevel)/.make/product.mk \ No newline at end of file diff --git a/README.md b/products/prototype/v1/README.md similarity index 100% rename from README.md rename to products/prototype/v1/README.md diff --git a/products/prototype/v1/docs/README.md b/products/prototype/v1/docs/README.md new file mode 100644 index 0000000..b1c49a6 --- /dev/null +++ b/products/prototype/v1/docs/README.md @@ -0,0 +1,12 @@ + +# Part template documents (version v1) + + +This the document collection of the product template. +1. Edit this README + - Change title & version! + +--- +## Documents + +- none \ No newline at end of file diff --git a/products/prototype/v1/requirements/README.md b/products/prototype/v1/requirements/README.md new file mode 100644 index 0000000..c4e4f3f --- /dev/null +++ b/products/prototype/v1/requirements/README.md @@ -0,0 +1,17 @@ + +# Product template requirements (version v1) + + +These are the requirements files of the product template. +1. Edit this README + - Change title & version! + +--- +## Requirements + +- none + +--- +## Constraints + +- none \ No newline at end of file From 8950d19d96045968f634eddec65d272801c5cc54 Mon Sep 17 00:00:00 2001 From: Uffke Drechsler Date: Thu, 7 May 2020 22:44:41 +0200 Subject: [PATCH 02/12] move to new repository structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now follows an excerpt of all added READMEs: General repository structure ---------------------------- - The project consists of *components* - *Components* reside inside the root of the repository - All *component* directories should feature a README.md - All *component* directories should feature a Makefile lifesensor │ ├── README.md : main README.md of the project ├── Makefile : Makefile executing targets in all components │ ├── CONTRIBUTING.md : information on how to contribute ├── LICENSE.md : license information │ ├── docs │ ├── README.md : list & description of project documents │ ├── Makefile : Makefile building/generating documents │ └── ... │ ├── : component name │ ├── README.md : general description of the component, │ │ its structure and a list of all subcomponents │ ├── Makefile : Makefile executing targets in all subcomponents │ │ │ ├── : subcomponent name │ │ ├── README.md : general description of the subcomponent │ │ │ and list of all subcomponent variants │ │ ├── Makefile : Makefile bundeling all variants │ │ │ │ │ ├── docs │ │ │ ├── README.md : list & description of subcomponent documents │ │ │ ├── Makefile : Makefile building/generating documents │ │ │ └── ... │ │ │ │ │ ├── : a variant is a non-exchangeable component │ │ │ ├── README.md : general description of the variant │ │ │ ├── Makefile : Makefile integrating component toolchain │ │ │ │ │ │ │ ├── docs │ │ │ │ ├── README.md : list & description of variant documents │ │ │ │ ├── Makefile : Makefile building/generating documents │ │ │ │ └── ... │ │ │ └── ... │ │ │ │ │ ├── │ │ │ └── ... │ │ └── ... │ │ │ ├── │ └── ... │ ├── │ └── ... └── ... - A README.md should link to all subdirectories that contain a README.md - Links to directories should always end with a slash (`/`) - Links should always have a relative prefix (`./`,`../`, ... ) - All README.md's should describe important Makefile targets - All README.md's should describe the structure of subdirectories - All Makefiles should support the common targets. - `help` - show information about targets and variables - `all` - run common jobs (like `build` & `test`) - `check` - run all check jobs - *check* jobs are environment validations - I.e. check if toolchain is installed - `setup` - run all setup jobs - *setup* jobs are environment configurations - I.e. define devices to flash - `build` - run all build jobs - should not be dependent on any `setup` target - `test` - run all test jobs - should not be dependent on any `setup` target - `clean` - delete everything build - `distclean` - delete everything generated - should reset the state prior execution of any target - All Makefiles should integrate Makefiles in subdirectories. - Targets that fall in one of these target categories should be named `-`. - I.e. `build-library`, `clean-library`, `test-library`, ... - A Makefile should include the [.common.mk](./.make/common.mk) Makefile to define these common targets. - simply add `include $(shell git rev-parse --show-toplevel)/.make/common.mk` as last statement in the Makefile. Docker component structure -------------------------- lifesensor/docker ├── README.md : mandatory README.md you are currently reading ├── Makefile : mandatory Makefile executing targets in all subdirs │ ├── : mandatory unique name of the image │ ├── README.md : mandatory general description of the image │ │ and elaboration of differences between version │ ├── Makefile : mandatory Makefile executing targets in all subdirs │ │ │ ├── : mandatory unique image version │ │ ├── README.md : mandatory version specific description of the image, │ │ │ should list all features │ │ ├── Makefile : mandatory Makefile for integration of the image │ │ ├── Dockerfile : mandatory Dockerfile to build image │ │ └── ... │ │ │ ├── │ │ └── ... │ └── ... │ ├── │ └── ... └── ... - *Docker images* of the LifeSensor project should have a dedicated directory here. - *Docker images* can be based on each other (see [base](./base/) image). - *Docker images* may compete for the same functionality. - Different [*parts*](../parts/) may use different *docker images* - *Docker images* should follow the directory scheme defined by the [*template image*](./.template/) - *Docker images* may contain any additional files & directories Parts component structure ------------------------- lifesensor/parts ├── README.md : mandatory README.md you are currently reading ├── Makefile : mandatory Makefile executing targets in all subdirs │ ├── : mandatory unique name of the part │ ├── README.md : mandatory general description of the part │ │ and elaboration of differences between version │ │ │ ├── : mandatory unique part version │ │ ├── README.md : mandatory version specific description of the part, │ │ │ should list all features │ │ │ │ │ ├── Makefile : mandatory Makefile for integration of the part │ │ │ │ │ ├── code : optional parent directory of any source code and build system files │ │ │ │ i.e. firmware │ │ │ ├── README.md : description of the source code │ │ │ └── ... │ │ │ │ │ ├── docs : optional parent directory for documents │ │ │ │ i.e. documentation, datasheets │ │ │ ├── README.md : description of documents │ │ │ └── ... │ │ │ │ │ ├── kicad : optional parent directory for kicad files │ │ │ │ i.e. project files, schematics │ │ │ ├── README.md : description of the kicad files │ │ │ └── ... │ │ │ │ │ ├── mechanics : optional parent directory for mechanic files │ │ │ │ i.e. 3D-printer models │ │ │ ├── README.md : description of the mechanics files │ │ │ └── ... │ │ │ │ │ ├── requirements : mandatory parent directory for requirements │ │ │ │ used by requirement management tools │ │ │ ├── README.md : description of requirements │ │ │ └── ... │ │ │ │ │ └── scripts : optional parent directory of part specific helper scripts │ │ │ i.e. documentation generation, calculators │ │ ├── README.md : description of scripts │ │ └── ... │ │ │ ├── │ │ └── ... │ └── ... │ ├── │ └── ... └── ... - *Parts* of the LifeSensor project should have a dedicated directory here. - *Parts* should not include other *parts*, instead they should refer to other *parts*. - *Parts* may compete for the same functionality. - Different [*products*](../products/) may choose different *parts* - *Parts* should follow the directory scheme defined by the [*template part*](./.template/) - *Parent directories* may contain any structure - Optional *parent directories* can be removed - Only non-breaking changes may be introduced as patch for a *part* - *Part versions* denote non-backwards-compatible variants of the same *part* - If major implementations change, consider creating a new *part* or *part version* - *Part versions* should never denote the state of the *part* - All *part versions* should provide their intended functionality Products component structure ---------------------------- lifesensor/products │ ├── README.md : mandatory README.md you are currently reading ├── Makefile : mandatory Makefile executing targets in all subdirs │ ├── : mandatory unique name of the product │ ├── README.md : mandatory general description of the product │ │ and elaboration of differences between version │ │ │ ├── : mandatory unique product version │ │ ├── README.md : mandatory version specific description of the product, │ │ │ should list all features │ │ │ │ │ ├── Makefile : mandatory Makefile for integration of the product │ │ │ │ │ ├── docs : optional parent directory for documents │ │ │ │ i.e. documentation, datasheets │ │ │ ├── README.md : description of documents │ │ │ └── ... │ │ │ │ │ └── requirements : mandatory parent directory for requirements │ │ │ used by requirement management tools │ │ ├── README.md : description of requirements │ │ └── ... │ │ │ ├── │ │ └── ... │ └── ... │ ├── │ └── ... └── ... - *Products* of the [*LifeSensor*](https://lifesensor.org) project should have a dedicated directory here. - *Products* should not include [*parts*](../parts/) or other *products* - *Products* may compete for the same functionality. - Different *products* may choose different [*parts*](../parts/) to achieve the same - *Products* should follow the directory scheme defined by the [*template product*](./.template/) - *Parent directories* may contain any structure - Optional *parent directories* can be removed - Only non-breaking changes may be introduced as patch for a *product* - *Products versions* denote non-backwards-compatible variants of the same *product* - If major implementations change, consider creating a new *product* or *product version* - *Products versions* should never denote the state of the *product* - All *product versions* should provide their intended functionality --- .Makefile.mk | 277 ----------- .Makefile.template | 113 ----- .gitcommitmsg | 30 -- .github/workflows/lifesensor.yml | 15 - .github/workflows/lifesensor_ci.yml | 21 - .github/workflows/lifesensor_test.yml | 15 - .github/workflows/push_pr.yml | 21 + .make/README.md | 69 +++ .make/common.mk | 135 ++++++ .make/docker.mk | 162 +++++++ .make/help.mk | 79 +++ .make/idf.mk | 139 ++++++ .make/part.mk | 3 + .make/product.mk | 37 ++ LICENSE.md | 1 + Makefile | 2 + README.md | 60 +++ Structure.md | 106 ++++ docker.old/.gitignore | 2 - docker.old/Makefile | 305 ------------ docker.old/README.md | 18 - docker.old/idf/Dockerfile | 47 -- docker.old/platformio/Dockerfile | 19 - docker.old/qemu/Dockerfile | 45 -- docker.old/vscode/Dockerfile | 25 - docker/.template/Makefile | 1 + docker/.template/README.md | 38 ++ docker/.template/v1/Dockerfile | 26 + docker/.template/v1/Makefile | 25 + docker/.template/v1/README.md | 45 ++ docker/Makefile | 2 + docker/README.md | 74 +++ docker/base/Makefile | 2 + docker/base/README.md | 32 ++ docker/base/v1/Dockerfile | 16 + docker/base/v1/Makefile | 25 + docker/base/v1/README.md | 38 ++ docker/idf/Makefile | 2 + docker/idf/README.md | 32 ++ docker/idf/v4.0/Dockerfile | 38 ++ docker/idf/v4.0/Makefile | 20 + docker/idf/v4.0/README.md | 35 ++ .../idf/v4.0}/idf_tools.py.patch | 0 docker/qemu/Makefile | 2 + docker/qemu/README.md | 30 ++ docker/qemu/esp-develop/Dockerfile | 49 ++ docker/qemu/esp-develop/Makefile | 23 + docker/qemu/esp-develop/README.md | 35 ++ docs/Makefile | 2 - docs/README.md | 6 + docs/lifesensor_logo.png | Bin 0 -> 174376 bytes parts/.template/Makefile | 2 + parts/.template/README.md | 40 ++ parts/.template/v1/Makefile | 1 + parts/.template/v1/README.md | 71 +++ parts/.template/v1/code/.gitignore | 1 + parts/.template/v1/code/Makefile | 17 + parts/.template/v1/code/README.md | 14 + parts/.template/v1/code/main.c | 5 + parts/.template/v1/docs/README.md | 12 + parts/.template/v1/kicad/README.md | 15 + .../v1/kicad/simulation/simulation-cache.lib | 54 +++ .../v1/kicad/simulation/simulation.kicad_pcb | 1 + .../v1/kicad/simulation/simulation.pro | 33 ++ .../v1/kicad/simulation/simulation.sch | 68 +++ .../v1/kicad/simulation/simulation.sch-bak | 4 + .../voltage_divider/voltage_divider-cache.lib | 21 + .../voltage_divider/voltage_divider.kicad_pcb | 1 + .../kicad/voltage_divider/voltage_divider.pro | 33 ++ .../kicad/voltage_divider/voltage_divider.sch | 55 +++ .../voltage_divider/voltage_divider.sch-bak | 55 +++ parts/.template/v1/mechanics/README.md | 12 + parts/.template/v1/requirements/README.md | 17 + parts/.template/v1/scripts/README.md | 0 parts/Makefile | 2 + parts/README.md | 115 +++++ parts/esp32-components/Makefile | 2 + parts/esp32-components/README.md | 43 +- parts/esp32-firmware/Makefile | 2 + parts/esp32-firmware/README.md | 39 ++ parts/esp32-firmware/prototype/CMakeLists.txt | 2 +- parts/esp32-firmware/prototype/Makefile | 5 +- .../prototype/components/channel | 1 + .../prototype/components/lifesensor_common | 1 + .../esp32-firmware/prototype/components/linux | 1 + .../esp32-firmware/prototype/components/spo2 | 1 + .../prototype/components/ulp_adc | 1 + parts/esp32-firmware/prototype/docs/Makefile | 21 + .../prototype/docs/esp32_overview.neato.gv | 0 .../prototype/docs}/esp32_overview.svg | 2 +- parts/esp32-firmware/qemu/CMakeLists.txt | 2 +- parts/esp32-firmware/qemu/Makefile | 82 +++- parts/esp32-firmware/qemu/components/channel | 1 + .../qemu/components/lifesensor_common | 1 + parts/esp32-firmware/qemu/components/linux | 1 + parts/esp32-firmware/qemu/components/spo2 | 1 + parts/esp32-firmware/qemu/components/ulp_adc | 1 + parts/esp32-firmware/test/.gitignore | 1 + parts/esp32-firmware/test/CMakeLists.txt | 9 +- parts/esp32-firmware/test/Makefile | 5 +- parts/esp32-firmware/test/components/channel | 1 + .../test/components/lifesensor_common | 1 + parts/esp32-firmware/test/components/linux | 1 + parts/esp32-firmware/test/components/spo2 | 1 + parts/esp32-firmware/test/components/ulp_adc | 1 + parts/interconnect/Makefile | 2 + parts/interconnect/README.md | 37 ++ parts/interconnect/v1/Makefile | 2 + parts/interconnect/v1/README.md | 44 ++ parts/interconnect/v1/docs/README.md | 11 + parts/interconnect/v1/docs/vendors.md | 13 + parts/interconnect/v1/kicad/README.md | 11 + .../kicad/interconnect/interconnect.kicad_pcb | 1 + .../v1/kicad/interconnect/interconnect.pro | 33 ++ .../v1/kicad/interconnect/interconnect.sch | 451 ++++++++++++++++++ products/.template/README.md | 40 ++ products/.template/v1/Makefile | 5 + products/.template/v1/README.md | 47 ++ products/.template/v1/docs/README.md | 12 + products/.template/v1/requirements/README.md | 17 + products/Makefile | 2 + products/README.md | 81 ++++ 122 files changed, 3079 insertions(+), 953 deletions(-) delete mode 100644 .Makefile.mk delete mode 100644 .Makefile.template delete mode 100644 .gitcommitmsg delete mode 100644 .github/workflows/lifesensor.yml delete mode 100644 .github/workflows/lifesensor_ci.yml delete mode 100644 .github/workflows/lifesensor_test.yml create mode 100644 .github/workflows/push_pr.yml create mode 100644 .make/README.md create mode 100644 .make/common.mk create mode 100644 .make/docker.mk create mode 100644 .make/help.mk create mode 100644 .make/idf.mk create mode 100644 .make/part.mk create mode 100644 .make/product.mk create mode 100644 LICENSE.md create mode 100644 Makefile create mode 100644 README.md create mode 100644 Structure.md delete mode 100644 docker.old/.gitignore delete mode 100644 docker.old/Makefile delete mode 100644 docker.old/README.md delete mode 100644 docker.old/idf/Dockerfile delete mode 100644 docker.old/platformio/Dockerfile delete mode 100644 docker.old/qemu/Dockerfile delete mode 100644 docker.old/vscode/Dockerfile create mode 100644 docker/.template/Makefile create mode 100644 docker/.template/README.md create mode 100644 docker/.template/v1/Dockerfile create mode 100644 docker/.template/v1/Makefile create mode 100644 docker/.template/v1/README.md create mode 100644 docker/Makefile create mode 100644 docker/README.md create mode 100644 docker/base/Makefile create mode 100644 docker/base/README.md create mode 100644 docker/base/v1/Dockerfile create mode 100644 docker/base/v1/Makefile create mode 100644 docker/base/v1/README.md create mode 100644 docker/idf/Makefile create mode 100644 docker/idf/README.md create mode 100644 docker/idf/v4.0/Dockerfile create mode 100644 docker/idf/v4.0/Makefile create mode 100644 docker/idf/v4.0/README.md rename {docker.old/idf => docker/idf/v4.0}/idf_tools.py.patch (100%) create mode 100644 docker/qemu/Makefile create mode 100644 docker/qemu/README.md create mode 100644 docker/qemu/esp-develop/Dockerfile create mode 100644 docker/qemu/esp-develop/Makefile create mode 100644 docker/qemu/esp-develop/README.md delete mode 100644 docs/Makefile create mode 100644 docs/README.md create mode 100644 docs/lifesensor_logo.png create mode 100644 parts/.template/Makefile create mode 100644 parts/.template/README.md create mode 100644 parts/.template/v1/Makefile create mode 100644 parts/.template/v1/README.md create mode 100644 parts/.template/v1/code/.gitignore create mode 100644 parts/.template/v1/code/Makefile create mode 100644 parts/.template/v1/code/README.md create mode 100644 parts/.template/v1/code/main.c create mode 100644 parts/.template/v1/docs/README.md create mode 100644 parts/.template/v1/kicad/README.md create mode 100644 parts/.template/v1/kicad/simulation/simulation-cache.lib create mode 100644 parts/.template/v1/kicad/simulation/simulation.kicad_pcb create mode 100644 parts/.template/v1/kicad/simulation/simulation.pro create mode 100644 parts/.template/v1/kicad/simulation/simulation.sch create mode 100644 parts/.template/v1/kicad/simulation/simulation.sch-bak create mode 100644 parts/.template/v1/kicad/voltage_divider/voltage_divider-cache.lib create mode 100644 parts/.template/v1/kicad/voltage_divider/voltage_divider.kicad_pcb create mode 100644 parts/.template/v1/kicad/voltage_divider/voltage_divider.pro create mode 100644 parts/.template/v1/kicad/voltage_divider/voltage_divider.sch create mode 100644 parts/.template/v1/kicad/voltage_divider/voltage_divider.sch-bak create mode 100644 parts/.template/v1/mechanics/README.md create mode 100644 parts/.template/v1/requirements/README.md create mode 100644 parts/.template/v1/scripts/README.md create mode 100644 parts/Makefile create mode 100644 parts/README.md create mode 100644 parts/esp32-components/Makefile create mode 100644 parts/esp32-firmware/Makefile create mode 100644 parts/esp32-firmware/README.md mode change 120000 => 100644 parts/esp32-firmware/prototype/Makefile create mode 120000 parts/esp32-firmware/prototype/components/channel create mode 120000 parts/esp32-firmware/prototype/components/lifesensor_common create mode 120000 parts/esp32-firmware/prototype/components/linux create mode 120000 parts/esp32-firmware/prototype/components/spo2 create mode 120000 parts/esp32-firmware/prototype/components/ulp_adc create mode 100644 parts/esp32-firmware/prototype/docs/Makefile rename docs/esp32_overview.gv => parts/esp32-firmware/prototype/docs/esp32_overview.neato.gv (100%) rename {docs => parts/esp32-firmware/prototype/docs}/esp32_overview.svg (99%) mode change 120000 => 100644 parts/esp32-firmware/qemu/Makefile create mode 120000 parts/esp32-firmware/qemu/components/channel create mode 120000 parts/esp32-firmware/qemu/components/lifesensor_common create mode 120000 parts/esp32-firmware/qemu/components/linux create mode 120000 parts/esp32-firmware/qemu/components/spo2 create mode 120000 parts/esp32-firmware/qemu/components/ulp_adc mode change 120000 => 100644 parts/esp32-firmware/test/Makefile create mode 120000 parts/esp32-firmware/test/components/channel create mode 120000 parts/esp32-firmware/test/components/lifesensor_common create mode 120000 parts/esp32-firmware/test/components/linux create mode 120000 parts/esp32-firmware/test/components/spo2 create mode 120000 parts/esp32-firmware/test/components/ulp_adc create mode 100644 parts/interconnect/Makefile create mode 100644 parts/interconnect/README.md create mode 100644 parts/interconnect/v1/Makefile create mode 100644 parts/interconnect/v1/README.md create mode 100644 parts/interconnect/v1/docs/README.md create mode 100644 parts/interconnect/v1/docs/vendors.md create mode 100644 parts/interconnect/v1/kicad/README.md create mode 100644 parts/interconnect/v1/kicad/interconnect/interconnect.kicad_pcb create mode 100644 parts/interconnect/v1/kicad/interconnect/interconnect.pro create mode 100644 parts/interconnect/v1/kicad/interconnect/interconnect.sch create mode 100644 products/.template/README.md create mode 100644 products/.template/v1/Makefile create mode 100644 products/.template/v1/README.md create mode 100644 products/.template/v1/docs/README.md create mode 100644 products/.template/v1/requirements/README.md create mode 100644 products/Makefile create mode 100644 products/README.md diff --git a/.Makefile.mk b/.Makefile.mk deleted file mode 100644 index db5d368..0000000 --- a/.Makefile.mk +++ /dev/null @@ -1,277 +0,0 @@ -# -# see .Makefile.template for help -# - -### variables ### - -VARIABLE+=ESPPORT -HELP_ESPPORT=which device to flash/monitor -export ESPPORT - -### internal variables ### - -# assume /etc/passwd holds right shell of user -USERSHELL=$(shell awk -F ':' '/^'$$(id -un)':/{print $$NF}' /etc/passwd) - -# export any additional docker options for docker Makefile -export DOCKEROPTS - -# assume we are in projectdir -PROJECTDIR=$(shell pwd) - -# assume dirname is projectname -PROJECT=$(notdir $(shell pwd)) - -# assume docker Makefile dir -DOCKERDIR=../docker - -# assume template path -TEMPLATEPATH=../.Makefile.template - -### default target ### -DEFAULT += help - -### menuconfig targets ### - -.PHONY: menuconfig -TARGET += menuconfig - -HELP_menuconfig = configure project -menuconfig: | check-docker - @make --no-print-directory -C $(DOCKERDIR) idf \ - WORKDIR=$(PROJECTDIR) \ - EXEC="idf.py menuconfig" - -### build targets ### - -.PHONY: reconfigure -TARGET += reconfigure -ALL += reconfigure -HELP_reconfigure = reconfigure project (rebuilds cmake files) -reconfigure: | check-docker - @make --no-print-directory -C $(DOCKERDIR) idf \ - WORKDIR=$(PROJECTDIR) \ - EXEC="idf.py reconfigure" - -.PHONY: build -TARGET += build -ALL += build -HELP_build = build project -build: | check-docker - @make --no-print-directory -C $(DOCKERDIR) idf \ - WORKDIR=$(PROJECTDIR) \ - EXEC="idf.py --ccache build" - -.PHONY: clean-build -CLEAN += clean-build -HELP_clean-build = remove generated files of project components -clean-build: | check-docker - rm -rf $(addprefix $(PROJECTDIR)/build/esp-idf/, $(shell find $(PROJECTDIR)/../components -mindepth 1 -maxdepth 1 -type d -printf "%f\n") main) - -.PHONY: distclean-build -DISTCLEAN += distclean-build -HELP_distclean-build = remove all generated files -distclean-build: - rm -f $(PROJECTDIR)/sdkconfig - rm -rf $(PROJECTDIR)/build - -### flash targets ### - -.PHONY: flash -TARGET += flash -HELP_flash = flash project to esp. Use ESPPORT=path to provide path to device or use make dev -flash: | check-flash - @make --no-print-directory -C $(DOCKERDIR) idf \ - WORKDIR=$(PROJECTDIR) \ - EXEC="sudo chgrp developer $(ESPPORT);\ - idf.py flash -p '$(ESPPORT)'" - -.PHONY: check-flash -CHECK += check-flash -HELP_check-flash = check env if flashing is possible -check-flash: | check-docker check-dev - -### monitor targets ### - -.PHONY: monitor -TARGET += monitor -HELP_monitor = connect to esp32 via serial. Use ESPPORT=path to provide path to device or use make dev -monitor: | check-monitor - @make --no-print-directory -C $(DOCKERDIR) idf \ - WORKDIR=$(PROJECTDIR) \ - EXEC="sudo chgrp developer $(ESPPORT); \ - idf.py monitor -p '$(ESPPORT)'" - -.PHONY: check-monitor -CHECK += check-monitor -HELP_check-monitor = check env if monitor is possible -check-monitor: | check-docker check-dev - -### vscode ### - -.PHONY: vscode -TARGET += vscode -HELP_vscode = starts vscode inside docker container -vscode: | check-docker - @make --no-print-directory -C $(DOCKERDIR) vscode \ - WORKDIR=$(PROJECTDIR) \ - EXEC="code .; bash" - -### qemu targets ### - -.PHONY: qemu -TARGET += qemu -HELP_qemu = start qemu and run build image -qemu: qemu-image | check-docker - @make --no-print-directory -C $(DOCKERDIR) qemu \ - WORKDIR=$(PROJECTDIR) \ - EXEC="echo \"############################\"; \ - echo \"# TO EXIT QEMU: #\"; \ - echo \"############################\"; \ - qemu-system-xtensa \ - -no-reboot \ - -nographic \ - -machine esp32 \ - -drive file=$(PROJECTDIR)/build/qemu.bin,if=mtd,format=raw \ - 2>&1 | tee build/qemu.log; \ - grep -q \"0 Failures\" build/qemu.log" - -.PHONY: qemu-image -TARGET_qemu += qemu-image -HELP_qemu-image = converts build image to image runnable by qemu -qemu-image: $(PROJECTDIR)/build/qemu.bin -.DELETE_ON_ERROR: $(PROJECTDIR)/build/qemu.bin -$(PROJECTDIR)/build/qemu.bin: $(PROJECTDIR)/build/$(PROJECT).bin - dd if=/dev/zero bs=1024 count=4096 of=$@ - dd if=$(PROJECTDIR)/build/bootloader/bootloader.bin bs=1 seek=$$((0x1000)) of=$@ conv=notrunc - dd if=$(PROJECTDIR)/build/partition_table/partition-table.bin bs=1 seek=$$((0x8000)) of=$@ conv=notrunc - dd if=$(PROJECTDIR)/build/$(PROJECT).bin bs=1 seek=$$((0x10000)) of=$@ conv=notrunc - -.PHONY: qemu-gdb -TARGET += qemu-gdb -HELP_qemu-gdb = start qemu and wait for gdb -qemu-gdb: qemu-image | check-docker - @make --no-print-directory -C $(DOCKERDIR) qemu \ - DOCKEROPTS="--name $(PROJECT).qemu" \ - WORKDIR=$(PROJECTDIR) \ - EXEC="echo \"###################################\"; \ - echo \"# TO START GDB RUN #\"; \ - echo \"# make PROJECT= gdb-qemu #\"; \ - echo \"# IN ANOTHER SHELL #\"; \ - echo \"###################################\"; \ - qemu-system-xtensa \ - -s -S \ - -no-reboot \ - -nographic \ - -machine esp32 \ - -drive file=build/qemu.bin,if=mtd,format=raw" - - -### gdb targets ### - -.PHONY: gdb-qemu -TARGET += gdb-qemu -gdb-HELP_qemu = start gdb and connect to qemu -gdb-qemu: $(PROJECTDIR)/build/$(PROJECT).elf | check-docker - docker exec -ti $(PROJECT).qemu \ - bash -ic "xtensa-esp32-elf-gdb $^ \ - -ex \"target remote :1234\" \ - -ex \"tb app_main\" \ - -ex \"c\"" - -### dev targets ### - -.PHONY: dev -TARGET += dev -HELP_dev = specify which USB device to connect to -dev: | check-dialog - export ESPPORT=$$((for dev in $$(ls /dev/serial/by-id); do echo "$$(readlink -f /dev/serial/by-id/$$dev) $$dev "; done ) \ - | dialog \ - --stdout \ - --menu "choose which device to use" 0 0 0 "none" "disable device passthrough"\ - --file /dev/stdin);\ - clear; \ - exec $(USERSHELL) - -.PHONY: check-dev -CHECK += check-dev -HELP_check-dev = check if device is specified -check-dev: - @1>&2 echo -n "checking if device is valid & specified..." -ifndef ESPPORT - @1>&2 echo - @1>&2 echo "##############################" - @1>&2 echo "# FLASH DEVICE NOT SPECIFIED #" - @1>&2 echo "##############################" - @1>&2 echo - @1>&2 echo "specify device by adding ESPPORT as parameter" - @1>&2 echo - @1>&2 echo "make $(MAKECMDGOALS) ESPPORT=/path/to/device" - @1>&2 echo - @1>&2 echo "or by running" - @1>&2 echo - @1>&2 echo "make dev" - @1>&2 echo - @exit 1 -endif -ifeq ($(shell ! test -c $(ESPPORT); echo $$?),0) - @1>&2 echo - @1>&2 echo "#############################" - @1>&2 echo "# FLASH DEVICE IS NOT VALID #" - @1>&2 echo "#############################" - @1>&2 echo - @1>&2 echo "the specified device ($(ESPPORT)) is not a character device!" - @1>&2 echo - @1>&2 echo "specify device by adding ESPPORT as parameter" - @1>&2 echo - @1>&2 echo "make $(MAKECMDGOALS) ESPPORT=/path/to/device" - @1>&2 echo - @1>&2 echo "or by running" - @1>&2 echo - @1>&2 echo "make dev" - @1>&2 echo - @exit 1 -endif - @1>&2 echo "SUCCESS" - -.PHONY: check-dialog -CHECK += check-dialog -HELP_check-dialog = check if dialog is installed -check-dialog: - @1>&2 echo -n "checking if dialog is installed..." -ifeq ($(shell which dialog 2> /dev/null),) - @1>&2 echo - @1>&2 echo "###########################" - @1>&2 echo "# DIALOG IS NOT INSTALLED #" - @1>&2 echo "###########################" - @1>&2 echo - @1>&2 echo "Consider installing dialog." - @1>&2 echo - @1>&2 echo "on debian based systems:" - @1>&2 echo " sudo apt-get install dialog" - @1>&2 echo - @1>&2 echo "on arch based systems:" - @1>&2 echo " sudo pacman -S dialog" - @1>&2 echo - @exit 1 -endif - @1>&2 echo "SUCCESS" - -### docker targets ### - -.PHONY: check-docker -CHECK += check-docker -HELP_check-docker = check if docker is usable -check-docker: - @make --no-print-directory -C $(DOCKERDIR) check - -### git targets - -.PHONY: setup-git -SETUP += setup-git -HELP_setup-git = setup git commit message -setup-git: - git config --replace-all commit.template .gitcommitmsg - git config --replace-all pull.rebase true - -include $(TEMPLATEPATH) diff --git a/.Makefile.template b/.Makefile.template deleted file mode 100644 index 647f1a8..0000000 --- a/.Makefile.template +++ /dev/null @@ -1,113 +0,0 @@ -############################################################################### -### MAKEFILE TEMPLATE ### -############################################################################### -# This Makefile template can be used to qickly generate help messages for make. -# It is based on dynamic variables (see make manual) -# -# THIS MAKEFILE NEEDS TO BE INCLUDED AFTER ALL DECLARATIONS -# -# predefined targets: -# default: is executed when no target is specified -# all: executes all steps -# check: check if targets can run -# setup: modify environment -# clean: remove generated files -# distclean: remove everything that was created by the toolchain -# -# to add your own targets to these predefined targets, -# just append your target name to the uppercase variable. -# I.e. to add your target 'build' to 'default' and 'all', do -# ALL+=build -# DEFAULT+=build -# -# own targets should be added to the 'TARGET' variable, so help can find them. -# I.e. to add your target 'build', do -# TARGET+=build -# -# if a target has subtargets, you can add them to the 'TARGET_' to -# indicate them as subtarget. -# I.e. to add your subtarget 'build-lib' of 'build', do -# TARGET_build+=build-lib -# -# to set the help text you have to set the variable 'HELP_' -# I.e to add the helptext for our 'build' target, do -# HELP_build=builds project -# -# if you want to define variables, just add them to 'VARIABLES' -# I.e to add the variable 'CXX', do -# VARIABLE+=CXX -# HELP_CXX=what compiler to use -# - -.DEFAULT_GOAL=default - -HELP_default = make $(DEFAULT) -.PHONY: default -default: $(DEFAULT) - -HELP_all = make $(ALL) -.PHONY: all -all: $(ALL) - -HELP_check = make $(CHECK) -.PHONY: check -check: $(CHECK) - -HELP_setup = make $(SETUP) -.PHONY: setup -setup: $(SETUP) - -HELP_clean = make $(CLEAN) -.PHONY: clean -clean: $(CLEAN) - -HELP_distclean = make clean $(DISTCLEAN) -.PHONY: distclean -distclean: clean $(DISTCLEAN) - -.PHONY: help -help: - @echo "--- help ---" - @[ -n "$(DEFAULT)" ] && echo -e "make \n\t$(HELP_default)" || true; - @[ -n "$(ALL)" ] && echo -e "make all \n\t$(HELP_all)" || true; - @[ -n "$(CHECK)" ] && echo -e "make check \n\t$(HELP_check)" || true; - @[ -n "$(SETUP)" ] && echo -e "make setup \n\t$(HELP_setup)" || true; - @[ -n "$(CLEAN)" ] && echo -e "make clean \n\t$(HELP_clean)" || true; - @[ -n "$(DISTCLEAN)" ] && echo -e "make distclean\n\t$(HELP_distclean)" || true; - @echo - @$(foreach \ - target, \ - $(TARGET), \ - echo "make $(target)"; \ - [ -n "$(HELP_$(target))" ] && echo -e "\t$(HELP_$(target))" || true; \ - ) - @$(foreach \ - target, \ - $(TARGET), \ - [ -n "$(TARGET_$(target))" ] && echo -e "\n--- $(target) sub targets ---" || true; \ - $(foreach \ - subtarget, \ - $(TARGET_$(target)), \ - echo "make $(subtarget)"; \ - [ -n "$(HELP_$(subtarget))" ] && echo -e "\t$(HELP_$(subtarget))" || true; \ - ) \ - ) - @$(foreach \ - main, \ - CHECK SETUP CLEAN DISTCLEAN, \ - [ -n "$($(main))" ] && echo -e "\n--- $(main) sub targets ---" || true; \ - $(foreach \ - target, \ - $($(main)), \ - echo "make $(target)"; \ - [ -n "$(HELP_$(target))" ] && echo -e "\t$(HELP_$(target))" || true; \ - ) \ - ) - @[ -n "$(VARIABLE)" ] && echo -e "\n--- VARIABLES ---" || true; - @$(foreach \ - variable, \ - $(VARIABLE), \ - echo "$(variable)"; \ - [ -n "$(HELP_$(variable))" ] && echo -e "\t$(HELP_$(variable))" || true; \ - echo -e "\t(currently: $($(variable)))"; \ - ) diff --git a/.gitcommitmsg b/.gitcommitmsg deleted file mode 100644 index cf06679..0000000 --- a/.gitcommitmsg +++ /dev/null @@ -1,30 +0,0 @@ -#makefile:fix -#makefile:feature -#makefile:refactor -#docker:fix -#docker:feature -#docker:refactor -#pio:fix -#pio:feature -#pio:refactor -#esp32:fix -#esp32:feature -#esp32:refactor -#ulp:fix -#ulp:feature -#ulp:refactor - -####################################################### -### A single commit should only be using one prefix ### -### : ### -### action descriptions: ### -### fix : corrected undesired effect ### -### feature : added functionality ### -### refactor : rewrote sth, no feature or fix ### -### for example ### -### ### -### esp32:feature added uart support ### -### added device dependencies and callback support ### -### ### -####################################################### - diff --git a/.github/workflows/lifesensor.yml b/.github/workflows/lifesensor.yml deleted file mode 100644 index 425f5d8..0000000 --- a/.github/workflows/lifesensor.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Build project lifesensor - -on: [push, pull_request] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - name: build idf image - run: make -C docker idf-image - - name: build idf project - run: make -C lifesensor DOCKEROPTS=--tty=false build diff --git a/.github/workflows/lifesensor_ci.yml b/.github/workflows/lifesensor_ci.yml deleted file mode 100644 index 0217697..0000000 --- a/.github/workflows/lifesensor_ci.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Project lifesensor_ci - -on: [push, pull_request] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - name: build idf image - run: make -C docker idf-image - - name: build qemu image - run: make -C docker qemu-image - - name: build idf project - run: make -C lifesensor_ci DOCKEROPTS=--tty=false build - - name: convert firmware image - run: make -C lifesensor_ci DOCKEROPTS=--tty=false qemu-image - - name: run qemu - run: make -C lifesensor_ci DOCKEROPTS=--tty=false qemu diff --git a/.github/workflows/lifesensor_test.yml b/.github/workflows/lifesensor_test.yml deleted file mode 100644 index 178ef73..0000000 --- a/.github/workflows/lifesensor_test.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Build project lifesensor_test - -on: [push, pull_request] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - name: build idf image - run: make -C docker idf-image - - name: build idf project - run: make -C lifesensor_test DOCKEROPTS=--tty=false build diff --git a/.github/workflows/push_pr.yml b/.github/workflows/push_pr.yml new file mode 100644 index 0000000..216c97e --- /dev/null +++ b/.github/workflows/push_pr.yml @@ -0,0 +1,21 @@ +name: Project LifeSensor + +on: [push, pull_request] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Build Docker Images + run: make -C docker build + - name: Build Parts + run: make -C parts DOCKER_VARS_OPTIONS=--tty=false build + - name: Test Parts + run: make -C parts DOCKER_VARS_OPTIONS=--tty=false test + - name: Build Products + run: make -C products DOCKER_VARS_OPTIONS=--tty=false build + - name: Test Products + run: make -C products DOCKER_VARS_OPTIONS=--tty=false test diff --git a/.make/README.md b/.make/README.md new file mode 100644 index 0000000..12a66d2 --- /dev/null +++ b/.make/README.md @@ -0,0 +1,69 @@ +# Makefiles + +This is a collection of Makefiles that may be included for some default behavior. + +## General Makefiles + +### [help.mk](./help.mk) +- generates help text as default target +- help text is generated by adding targets & variables to dynamic variables +- variables + - `TARGETS` + - tracks targets that should appear in help text + - `TARGETS_` + - tracks targets that should appear as a subtarget of `` + - `VARIABLES` + - tracks groups of variables that should appear in help text + - `VARIABLES_` + - tracks variables that should appear as part og the `` group in help text + - `HELP_` + - if `` is in `TARGETS` or `VARIABLES` it is used as help text + + + +### [common.mk](./common.mk) +- common behavior for all Makefiles + - includes `help.mk` + - should be used as base if new specific Makefile is added +- creates common targets + - `all` + - `build` + - `test` + - `check` + - `setup` + - `clean` + - `distclean` + - `docker` + - starts docker container + - onlt exists if `DOCKER_IMAGE` is specified +- variables + - `DEPENDS` + - array of directories + - if common target is executed, make will execute the same target in these directories prior to executing it in the current Makefile + - `DOCKER_IMAGE` + - default docker image to use if target should be executed in docker + - `DOCKER_` + - custom docker image for the target `` + - `DOCKER` + - tracks targets which should be executed inside the specified docker image + - when one target is executed in docker, dependencies are also executed in docker if not specified otherwise + - `NO_DOCKER` + - tracks targets which shall not be executed inside docker + +## Specific Makefiles + +### [docker.mk](./docker.mk) + +- common behavior for all docker images + +### [idf.mk](./idf.mk) + +- common behavior for all idf framework related projects + +### [part.mk](./part.mk) + +- common behaviour for all parts + +### [product.mk](./product.mk) + +- common behaviour for all products diff --git a/.make/common.mk b/.make/common.mk new file mode 100644 index 0000000..0518c71 --- /dev/null +++ b/.make/common.mk @@ -0,0 +1,135 @@ +.DEFAULT_GOAL := help +HELP ?= LifeSensor Makefile + +VARIABLES += MAKEFILE +HELP_MAKEFILE := Makefile options + +VARIABLES_MAKEFILE += DEPENDS +HELP_DEPENDS ?= dependencies that should be run prior to this Makefile +DEPENDS ?= #empty + +VARIABLES_MAKEFILE += DOCKER +HELP_DOCKER := run target inside docker (disable with 'DOCKER=') +DOCKER ?= #empty + +VARIABLES_MAKEFILE += DOCKER_IMAGE +HELP_DOCKER_IMAGE := docker image to use for targets +DOCKER_IMAGE ?= #empty + +VARIABLES_MAKEFILE += DOCKER_VARS +HELP_DOCKER_VARS := variables to pass to docker Makefile when running docker as shell +DOCKER_VARS ?= #empty + +ifeq ($(find OPTIONS, $(DOCKER_VARS)),) +DOCKER_VARS += OPTIONS +DOCKER_VARS_OPTIONS ?= #empty +# for github actions set to +# DOCKER_VARS_OPTIONS := --tty=false +endif + +ifeq ($(find WORKDIR, $(DOCKER_VARS)),) +DOCKER_VARS += WORKDIR +DOCKER_VARS_WORKDIR := $(shell pwd) +endif + +ifeq ($(find MOUNT, $(DOCKER_VARS)),) +DOCKER_VARS += MOUNT +DOCKER_VARS_MOUNT := $(shell git rev-parse --show-toplevel) +endif + +TARGETS_common += all +TARGETS_common += build +TARGETS_common += test +TARGETS_common += check +TARGETS_common += setup +TARGETS_common += clean +TARGETS_common += distclean + +define TEMPLATE_DEPEND_TARGET +.PHONY: $3 +TARGETS_$2 := $3 $(TARGETS_$2) +HELP_$3 := run 'make -C $1 $4' +NO_DOCKER += $3 +$3: + $(MAKE) -C $1 $4 +endef + +$(foreach \ + SUBDIR, \ + $(DEPENDS), \ + $(foreach \ + TARGET, \ + $(TARGETS_common), \ + $(eval $(call TEMPLATE_DEPEND_TARGET,$(SUBDIR),$(TARGET),$(TARGET)-$(SUBDIR),$(TARGET))) \ + ) \ +) + +define TEMPLATE_COMMON_TARGET +.PHONY: $1 +HELP_$1 ?= run common $1 jobs ($2) +NO_DOCKER += $1 +$1: $(TARGETS_$1) +endef + +TARGETS_distclean := clean $(TARGETS_distclean) +HELP_all := run common jobs ($(TARGETS_all)) +$(foreach \ + TARGET, \ + $(TARGETS_common), \ + $(eval $(call TEMPLATE_COMMON_TARGET,$(TARGET),$(TARGETS_$(TARGET)))) \ +) + +.PHONY: docker +ifdef DOCKER_IMAGE +TARGETS += docker +HELP_docker := enter docker container +docker: + $(MAKE) -C $(shell git rev-parse --show-toplevel)/docker/$(DOCKER_IMAGE) run \ + WORKDIR=$(shell pwd) \ + MOUNT=$(shell git rev-parse --show-toplevel) \ + EXEC=bash +else +docker: ; +endif + +define TEMPLATE_DOCKER_TARGET +$(if $(DOCKER_$1),,$(if $(DOCKER_IMAGE), $(eval DOCKER_$1 := $(DOCKER_IMAGE)),$(error TARGET '$1' MISSES DOCKER IMAGE!. SET 'DOCKER_$1 = '))) +DOCKER_IMAGES += $(DOCKER_$1) +$1: | docker-$(DOCKER_$1) docker-info +$1: SHELL := $(shell $(MAKE) -sC $$(git rev-parse --show-toplevel)/docker/$(DOCKER_$1) shell $(foreach VAR,$(DOCKER_VARS),$(VAR)=$(DOCKER_VARS_$(VAR))) | grep "docker run" ) +endef + +$(foreach \ + TARGET, \ + $(DOCKER), \ + $(eval $(call TEMPLATE_DOCKER_TARGET,$(TARGET))) \ +) + +define TEMPLATE_DOCKER_IMAGE +docker-$1: SHELL := /bin/bash +docker-$1: + $(MAKE) -C $(shell git rev-parse --show-toplevel)/docker/$1 build +endef + +.PHONY: docker-info +docker-info: + $(info RUNNING IN DOCKER: $(SHELL)) + +$(foreach \ + IMAGE, \ + $(sort $(DOCKER_IMAGES)), \ + $(eval $(call TEMPLATE_DOCKER_IMAGE,$(IMAGE))) \ +) + +define TEMPLATE_NODOCKER_TARGET +$1: SHELL := /bin/bash +endef + +$(foreach \ + TARGET, \ + $(sort $(NO_DOCKER)), \ + $(eval $(call TEMPLATE_NODOCKER_TARGET,$(TARGET))) \ +) + +TARGETS := $(TARGETS_common) $(TARGETS) +include $(shell git rev-parse --show-toplevel)/.make/help.mk diff --git a/.make/docker.mk b/.make/docker.mk new file mode 100644 index 0000000..f35644a --- /dev/null +++ b/.make/docker.mk @@ -0,0 +1,162 @@ +HELP := LifeSensor docker image & container + +COMMIT := $(shell git rev-list --all --abbrev-commit -1 -- $(realpath $(firstword $(MAKEFILE_LIST)))) +ifndef COMMIT +COMMIT := unknown +$(warning COMMIT OF $(realpath $(firstword $(MAKEFILE_LIST))) UNDEFINED!) +endif + +VERSION := $(shell basename $$(dirname $(realpath $(firstword $(MAKEFILE_LIST))))) +ifndef VERSION +VERSION := unknown +$(warning VERSION OF $(realpath $(firstword $(MAKEFILE_LIST))) UNDEFINED!) +endif + +NAME := $(shell basename $$(dirname $$(dirname $(realpath $(firstword $(MAKEFILE_LIST)))))) +ifndef NAME +NAME := unknown +$(error NAME OF $(realpath $(firstword $(MAKEFILE_LIST))) UNDEFINED!) +endif + +VARIABLES += DOCKER_IMAGE +HELP_DOCKER_IMAGE := docker image options + +VARIABLES_DOCKER_IMAGE += IMAGE +HELP_IMAGE := name of the docker image +IMAGE := lifesensor/$(NAME)/$(VERSION) + +VARIABLES_DOCKER_IMAGE += TAG +HELP_TAG := tag of the docker image +TAG ?= latest + + +VARIABLES += DOCKER_BUILD +HELP_DOCKER_BUILD := docker build options + +VARIABLES_DOCKER_BUILD += $(ARGS) + +VARIABLES_DOCKER_BUILD += BUILD +HELP_BUILD := additional docker build options +BUILD ?= #empty + +VARIABLES += DOCKER_RUN +HELP_DOCKER_RUN := docker run options + +VARIABLES_DOCKER_RUN += OPTIONS +HELP_OPTIONS := additional docker run options +OPTIONS ?= #empty + +VARIABLES_DOCKER_RUN += VOLUMES +HELP_VOLUMES := volumes to mount inside container +VOLUMES ?= #empty + +VARIABLES_DOCKER_RUN += WORKDIR +HELP_WORKDIR := where to start in docker volume +WORKDIR ?= #empty + +VARIABLES_DOCKER_RUN += ENVS +HELP_ENVS := additional environment variables to set +ENVS ?= #empty + +VARIABLES_DOCKER_RUN += EXEC +HELP_EXEC := additional environment variables to set +EXEC ?= /bin/bash + +VARIABLES_DOCKER_RUN += MOUNT +HELP_MOUNT := what directory to mount into the container +MOUNT ?= #empty + +VARIABLES_DOCKER_RUN += DEVICE +HELP_DEVICE := what device to pass into the container +DEVICE ?= #empty + +.PHONY: build-image +TARGETS_build += build-image +HELP_build-image := build docker image '$(IMAGE):$(TAG)' +build-image: | check-docker + docker build \ + $(foreach ARG,$(ARGS),--build-arg=$(ARG)=$($(ARG)) ) \ + $(BUILD) \ + -t "$(IMAGE):$(COMMIT)" \ + . + docker image tag "$(IMAGE):$(COMMIT)" "$(IMAGE):$(TAG)" + +.PHONY: run +TARGETS += run +HELP_run := run docker image '$(IMAGE):$(TAG)' +run: build | check-docker + docker run --rm -ti --hostname $(NAME) --name $(NAME)\ + $(foreach VOLUME, $(VOLUMES),-v "$(VOLUME)" ) \ + $(foreach ENV, $(ENVS),-e "$(ENV)" ) \ + $(if $(WORKDIR),-w "$(WORKDIR)") \ + $(if $(DEVICE),--device "$(DEVICE)") \ + $(if $(MOUNT),-v $(abspath $(MOUNT)):$(abspath $(MOUNT)):rw) \ + $(OPTIONS) \ + "$(IMAGE):$(TAG)" \ + /bin/bash -ic '$(EXEC)' + +.PHONY: shell +TARGETS += shell +HELP_shell := prints command used by run without executing anything +shell: + echo docker run --rm -ti --hostname $(NAME) --name $(NAME)\ + $(foreach VOLUME, $(VOLUMES),-v "$(VOLUME)" ) \ + $(foreach ENV, $(ENVS),-e "$(ENV)" ) \ + $(if $(WORKDIR),-w "$(WORKDIR)") \ + $(if $(DEVICE),--device "$(DEVICE)") \ + $(if $(MOUNT),-v $(abspath $(MOUNT)):$(abspath $(MOUNT)):rw) \ + $(OPTIONS) \ + "$(IMAGE):$(TAG)"\ + /bin/bash -i + +.PHONY: exec +TARGETS += exec +HELP_run := execute command in docker container '$(NAME)' +exec: build | check-docker + docker exec -ti $(NAME) /bin/bash -ic '$(EXEC)' + +.PHONY: clean-volume +TARGETS_clean += clean-volume +HELP_clean-volume := delete docker volumes +clean-volume: | check-docker + $(foreach \ + VOLUME, \ + $(shell echo "$(VOLUMES)"| tr ' ' '\n' | grep -o '^[^/][^:]*'), \ + docker volume rm -f $(VOLUME); \ + ) + +.PHONY: distclean-image +TARGETS_distclean += distclean-image +HELP_distclean-image := delete docker image +distclean-image: | check-docker + for parent in $$(docker images $(IMAGE) -q | sort -u); \ + do \ + echo $$parent; \ + for image in $$(docker images -q --filter since=$$parent);\ + do \ + docker history $$image | grep -q $$parent && echo $$image; \ + done; \ + done | sort -u | xargs -r docker image rm -f + +.PHONY: check-docker +check-docker: +ifeq ($(shell which docker 2> /dev/null),) + @1>&2 echo "########################################" + @1>&2 echo "# DOCKER DOES NOT SEEM TO BE INSTALLED #" + @1>&2 echo "########################################" + @1>&2 echo "the docker binary could not be found in your PATH!" + @1>&2 echo "This usally means docker is simply not installed." + @exit 1 +endif +ifneq ($(shell groups | grep -q docker),) + @1>&2 echo "##############################################" + @1>&2 echo "# YOUR USER IS NOT PART OF THE DOCKER GROUP! #" + @1>&2 echo "##############################################" + @1>&2 echo "This means you need to execute every docker command with sudo" + @1>&2 echo "To avoid this, please add your user ($$USER) to the docker group with the following command:" + @1>&2 echo " sudo usermod -a -G docker $$USER" + @1>&2 echo "you may need to login again to apply the settings" + @exit 1 +endif + +include $(shell git rev-parse --show-toplevel)/.make/common.mk \ No newline at end of file diff --git a/.make/help.mk b/.make/help.mk new file mode 100644 index 0000000..cd9228e --- /dev/null +++ b/.make/help.mk @@ -0,0 +1,79 @@ +################################################################################ +### MAKEFILE HELP TEMPLATE ### +################################################################################ +# This Makefile template can be used to qickly generate help messages for make. +# It is based on dynamic variables (see make manual) +# +# THIS MAKEFILE NEEDS TO BE INCLUDED AFTER ALL DECLARATIONS! +# +# own targets should be added to the 'TARGETS' variable, so help can find them. +# I.e. to add your target 'bar', do +# TARGETS += bar +# +# if a target has subtargets, you can add them to 'TARGETS_' to +# indicate them as subtarget. +# I.e. to add your target 'bar-foo' to 'bar', do +# TARGETS_bar += bar-foo +# +# you may set the HELP variable to print a header in the help output +# I.e. HELP := This Makefile does something +# +# for a target specific help text you have to set the variable 'HELP_' +# I.e. HELP_bar := helptext for bar +# +# if you want to define variables, you have to create a variable group by +# adding the group's name to the variable 'VARIABLES' +# I.e to add the variable group 'BUILDOPTION', do +# VARIABLES += BUILDOPTION +# HELP_BUILDOPTION := build options +# +# you can now add variables to your new group +# I.e to add the variable 'CXX' to the group 'BUILDOPTION', do +# VARIABLE_BUILDOPTION += CXX +# HELP_CXX := what compiler to use +# + +.PHONY: help +help: + @echo "--- help ---" + @[ -n "$(HELP)" ] && echo -e "$(HELP)\n" || true; + @$(foreach \ + target, \ + $(TARGETS), \ + [ -n "$(HELP_$(target))" ] && echo "make $(target)" || true; \ + [ -n "$(HELP_$(target))" ] && echo -e "\t$(HELP_$(target))" || true; \ + ) + @echo -e "\n--- further help ---" + @$(foreach \ + target, \ + $(TARGETS), \ + [ -n "$(TARGETS_$(target))" ] && echo -e "make help-$(target)\n\thelp for $(target) subtargets" || true; \ + $(foreach \ + subtarget, \ + $(TARGETS_$(target)), \ + ) \ + ) + @$(foreach \ + group, \ + $(VARIABLES), \ + [ -n "$(VARIABLES_$(group))" ] && echo -e "\n--- $(HELP_$(group)) ---" || true; \ + $(foreach \ + variable, \ + $(VARIABLES_$(group)), \ + echo "$(variable)"; \ + [ -n "$(HELP_$(variable))" ] && echo -e "\t$(HELP_$(variable))" || true; \ + echo -e "\t(currently: $($(variable)))"; \ + ) \ + ) + +.PHONY: help-% +help-%: + @echo "--- help for target $(@:help-%=%) ---" + @[ -n "$(HELP)" ] && echo -e "$(HELP)\n" || true; + @[ -n "$(TARGETS_$(@:help-%=%))" ] && echo -e "\n--- $(@:help-%=%) targets ---" || true; + @$(foreach \ + target, \ + $(TARGETS_$(@:help-%=%)), \ + [ -n "$(HELP_$(target))" ] && echo "make $(target)" || true; \ + [ -n "$(HELP_$(target))" ] && echo -e "\t$(HELP_$(target))" || true; \ + ) \ No newline at end of file diff --git a/.make/idf.mk b/.make/idf.mk new file mode 100644 index 0000000..b057567 --- /dev/null +++ b/.make/idf.mk @@ -0,0 +1,139 @@ + +DOCKER_IMAGE := idf/v4.0 + +# load DEVICE configuration +-include .setup-device.mk +# delete DEVICE configuration if invalid +ifeq ($(shell ! test -c $(DEVICE); echo $$?),0) +$(shell rm -f .setup-device.mk) +undefine DEVICE +else +export DEVICE +endif + +DOCKER_VARS += DEVICE +DOCKER_VARS_DEVICE := $(DEVICE) + +DOCKER_VARS += ENVS +DOCKER_VARS_ENVS := "IDF_PORT=$(DEVICE)" + +IDF = idf.py --ccache + +VARIABLES += DOCKEROPTIONS +HELP_DOCKEROPTIONS := Docker options + +VARIABLES_DOCKEROPTIONS += DEVICE +HELP_DEVICE := specifies which device to pass to docker + +TARGETS_all += build +TARGETS_all += test + +.PHONY: menuconfig +TARGETS += menuconfig +HELP_menuconfig = configure project +DOCKER += menuconfig +menuconfig: + $(IDF) menuconfig + +.PHONY: reconfigure +TARGET += reconfigure +HELP_reconfigure = reconfigure project (rebuilds cmake files) +DOCKER += reconfigure +reconfigure: + $(IDF) reconfigure + +.PHONY: build-firmware +TARGETS_build += build-firmware +HELP_build-firmware = build firmware files +DOCKER += build-firmware +build-firmware: + $(IDF) all + +.PHONY: clean-build +TARGETS_clean += clean-build +HELP_clean-build = remove generated files of project components +clean-build: + rm -rf build/esp-idf/ + +.PHONY: distclean-build +TARGETS_distclean += distclean-build +HELP_distclean-build = remove all generated files +distclean-build: + rm -f sdkconfig + rm -rf build + rm -rf .setup-device.mk + +.PHONY: flash +TARGETS += flash +HELP_flash = flash project to esp. Use DEVICE=path to provide path to device or use make setup +DOCKER += flash +flash: | check-device + $(IDF) flash -p '$(DEVICE)' + +.PHONY: monitor +TARGETS += monitor +HELP_monitor = connect to esp32 via serial. Use DEVICE=path to provide path to device or use make dev +DOCKER += monitor +monitor: | check-device + $(IDF) monitor -p '$(DEVICE)' + +.PHONY: check-device +TARGETS_check += check-device +HELP_check-device = check if valid device is specified +NO_DOCKER += check-device +check-device: +ifndef DEVICE + @1>&2 echo "##############################" + @1>&2 echo "# FLASH DEVICE NOT SPECIFIED #" + @1>&2 echo "##############################" + @1>&2 echo "specify device by adding DEVICE as parameter" + @1>&2 echo " make $(MAKECMDGOALS) DEVICE=/path/to/device" + @1>&2 echo "or by running" + @1>&2 echo " make setup" + @exit 1 +endif +ifeq ($(shell ! test -c $(DEVICE); echo $$?),0) + @1>&2 echo "#############################" + @1>&2 echo "# FLASH DEVICE IS NOT VALID #" + @1>&2 echo "#############################" + @1>&2 echo "the specified device '$(DEVICE)' is not a character device!" + @1>&2 echo "specify device by adding DEVICE as parameter" + @1>&2 echo " make $(MAKECMDGOALS) DEVICE=/path/to/device" + @1>&2 echo "or by running" + @1>&2 echo " make setup" + @exit 1 +endif + @exit 0 + +.PHONY: check-dialog +TARGETS_check += check-dialog +HELP_check-dialog = check if dialog is installed +NO_DOCKER += check-dialog +check-dialog: +ifeq ($(shell which dialog 2> /dev/null),) + @1>&2 echo "###########################" + @1>&2 echo "# DIALOG IS NOT INSTALLED #" + @1>&2 echo "###########################" + @1>&2 echo "Consider installing dialog." + @1>&2 echo "on debian based systems:" + @1>&2 echo " sudo apt-get install dialog" + @1>&2 echo "on arch based systems:" + @1>&2 echo " sudo pacman -S dialog" + @exit 1 +endif + @exit 0 + +.PHONY: setup-device +TARGETS_setup += setup-device +HELP_setup-device = sets device +NO_DOCKER += setup-device +setup-device: | check-dialog + echo -n DEVICE= > .setup-device.mk + for dev in $$(ls /dev/serial/by-id); do echo "$$(readlink -f /dev/serial/by-id/$$dev) $$dev "; done \ + | dialog \ + --stdout \ + --menu "choose which device to use" 0 0 0 "none" "disable device passthrough"\ + --file /dev/stdin\ + >> .setup-device.mk || rm -f .setup-device.mk + +include $(shell git rev-parse --show-toplevel)/.make/part.mk \ No newline at end of file diff --git a/.make/part.mk b/.make/part.mk new file mode 100644 index 0000000..c2ca6ee --- /dev/null +++ b/.make/part.mk @@ -0,0 +1,3 @@ +HELP ?= LifeSensor part + +include $(shell git rev-parse --show-toplevel)/.make/common.mk \ No newline at end of file diff --git a/.make/product.mk b/.make/product.mk new file mode 100644 index 0000000..8fbf315 --- /dev/null +++ b/.make/product.mk @@ -0,0 +1,37 @@ +HELP := LifeSensor product + +TARGET_default := all +TARGET_all := build test + +VARIABLE += PARTS +HELP_PARTS := parts this product uses + +.phony: build-requirements +TARGET_build += build-requirements +HELP_build-requirements := build requirements documents +build-requirements: + #todo + +.phony: test-requirements +TARGET_test += test-requirements +HELP_test-requirements := test if requirements are fullfilled +test-requirements: + #todo + +.phony: clean-requirements +TARGET_clean += clean-requirements +HELP_clean-requirements := clean generated requirement files +clean-requirements: + #todo + +define PART_TEMPLATE +.phony: $1-parts +TARGET_$1 += $1-parts +HELP_$1-parts := run make $1 for all parts +$1-parts: + $(foreach PART,$(PARTS),make -C ../../../parts/$(PART) $1) +endef + +$(foreach TARGET, build test clean distclean, $(eval $(call PART_TEMPLATE,$(TARGET)))) + +include $(shell git rev-parse --show-toplevel)/.make/common.mk \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..89b59fc --- /dev/null +++ b/LICENSE.md @@ -0,0 +1 @@ +# License information \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7f48ff4 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +DEPENDS = $(dir $(wildcard */Makefile)) +include $(shell git rev-parse --show-toplevel)/.make/common.mk \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9c59c45 --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# LifeSensor Project - health devices for all + +![LifeSensor logo](./docs/lifesensor_logo.png) + +The [*LifeSensor*](https://lifesensor.org) project is an open-source, modular, low-cost bio sensing platform. + +- This project is a collection of [*parts*](./parts/) and [*products*](./products/). +- [*Parts*](./parts/) are components that provide a set of features. +- [*Products*](./products/) are [*LifeSensor*](https://lifesensor.org) devices consisting of [*parts*](./parts/). +- [*Parts*](./parts/) are subjects to requirements and constraints imposed by [*products*](./products/) and other [*parts*](./parts/). + +--- +## Directories + +See [Structure.md](./Structure.md) for an overview on how this repository is organized. + +### [docker/](./docker/) +- Docker images used by this project +- Every used toolchain exists as docker image + +### [docs/](./docs/) +- Documents regarding the whole project + +### [parts/](./parts/) +- [*LifeSensor*](https://lifesensor.org) components used to build [*products*](./products/) + +### [products/](./products/) +- [*LifeSensor*](https://lifesensor.org) devices consisting of [*parts*](./parts/) + +--- +## Makefile + +This project makes heavy use of Makefiles. +See [Structure.md](./Structure.md) & [./.make](./.make) for more information. + + +- `make help` + - show Makefile options +- `make all` + - build & test all components +- `make build` + - build all components +- `make test` + - test all components +- `make clean` + - clean all components +- `make distclean` + - distclean all components + +--- +## Contributing +Please see +[CONTRIBUTING.md](./CONTRIBUTING.md) +for more information on how to contribute. + +--- +## License +Please see +[LICENSE.md](./LICENSE.md) +for more information on the used licenses. diff --git a/Structure.md b/Structure.md new file mode 100644 index 0000000..58ffd2a --- /dev/null +++ b/Structure.md @@ -0,0 +1,106 @@ +# Project structure & rules + + + +--- +## General project structure +- The project consists of *components* +- *Components* reside inside the root of the repository +- All *component* directories should feature a README.md +- All *component* directories should feature a Makefile + +``` +lifesensor +│ +├── README.md : main README.md of the project +├── Makefile : Makefile executing targets in all components +│ +├── CONTRIBUTING.md : information on how to contribute +├── LICENSE.md : license information +│ +├── docs +│   ├── README.md : list & description of project documents +│   ├── Makefile : Makefile building/generating documents +│ └── ...   +│ +├── : component name +│   ├── README.md : general description of the component, +│ │ its structure and a list of all subcomponents +│   ├── Makefile : Makefile executing targets in all subcomponents +│ │ +│   ├── : subcomponent name +│ │ ├── README.md : general description of the subcomponent +│ │ │ and list of all subcomponent variants +│ │   ├── Makefile : Makefile bundeling all variants +│ │ │ +│   │   ├── docs +│ │ │   ├── README.md : list & description of subcomponent documents +│ │ │   ├── Makefile : Makefile building/generating documents +│ │ │ └── ...   +│ │ │ +│ │   ├── : a variant is a non-exchangeable component +│ │ │ ├── README.md : general description of the variant +│ │ │   ├── Makefile : Makefile integrating component toolchain +│ │ │ │ +│   │   │   ├── docs +│ │ │ │   ├── README.md : list & description of variant documents +│ │ │ │   ├── Makefile : Makefile building/generating documents +│ │ │ │ └── ...   +│ │ │ └── ... +│ │ │ +│ │   ├── +│ │ │ └── ... +│ │ └── ...   +│ │ +│   ├── +│ └── ...   +│ +├── +│ └── ...   +└── ...   +``` + +--- +## README.md +- A README.md should link to all subdirectories that contain a README.md +- Links to directories should always end with a slash (`/`) + - I.e `[dir/](./dir/)` +- Links should always have a relative prefix (`./`,`../`, ... ) + - I.e `[file.txt](./file.txt)` +- All README.md's should describe important Makefile targets +- All README.md's should describe the structure of subdirectories + +--- +## Makefiles +- All Makefiles should support the common targets. + - `help` + - show information about targets and variables + - `all` + - run common jobs (like `build` & `test`) + - `check` + - run all check jobs + - *check* jobs are environment validations + - I.e. check if toolchain is installed + - `setup` + - run all setup jobs + - *setup* jobs are environment configurations + - I.e. define devices to flash + - `build` + - run all build jobs + - should not be dependent on any `setup` target + - `test` + - run all test jobs + - should not be dependent on any `setup` target + - `clean` + - delete everything build + - `distclean` + - delete everything generated + - should reset the state prior execution of any target +- All Makefiles should integrate Makefiles in subdirectories. +- Targets that fall in one of these target categories should be named +`-`. + - I.e. `build-library`, `clean-library`, `test-library`, ... +- A Makefile should include the [.common.mk](./.make/common.mk) Makefile +to define these common targets. + - simply add `include $(shell git rev-parse --show-toplevel)/.make/common.mk` +as last statement in the Makefile. diff --git a/docker.old/.gitignore b/docker.old/.gitignore deleted file mode 100644 index 499b1e8..0000000 --- a/docker.old/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -**/Dockerfile.log -.check* diff --git a/docker.old/Makefile b/docker.old/Makefile deleted file mode 100644 index 6a96160..0000000 --- a/docker.old/Makefile +++ /dev/null @@ -1,305 +0,0 @@ -# -# see ../.Makefile.template for help -# - -### variables ### - -VARIABLE += IDF_VERSION -HELP_IDF_VERSION = which idf version to use -IDF_VERSION=v4.0 - -VARIABLE += QEMU_VERSION -HELP_QEMU_VERSION = which qemu version to use -QEMU_VERSION=esp-develop - -VARIABLE += ESPPORT -HELP_ESPPORT = which device to map into container - -ifdef ESPPORT -DOCKER_DEV=--device $(ESPPORT) -endif - -VARIABLE += UID -HELP_UID = user id of user inside of container -UID=$(shell id -u) - -VARIABLE += GID -HELP_GID = group id of user inside of container -GID=$(shell id -g) - -VARIABLE += EXEC -HELP_EXEC = what to execute in the container -EXEC=bash - -VARIABLE += DOCKEROPTS -HELP_DOCKEROPTS = additional docker options - -VARIABLE += WORKDIR -HELP_WORKDIR = Where to enter docker container -WORKDIR=$(shell pwd)/.. - -### docker build targets ### - -.PHONY: idf-image -ALL += idf-image -DEFAULT += idf-image -TARGET_idf += idf-image -HELP_idf-image = builds docker idf image -idf-image: | check-docker - docker build \ - --build-arg=idf_version=$(IDF_VERSION) \ - --build-arg=UID=$(UID) \ - --build-arg=GID=$(GID) \ - -t "lifesensor/idf:$(IDF_VERSION)" \ - idf - -.PHONY: qemu-image -ALL += qemu-image -DEFAULT += qemu-image -TARGET_qemu += qemu-image -HELP_qemu-image = buils docker qemu image -qemu-image: idf-image | check-docker - docker build \ - --build-arg=idf_version=$(IDF_VERSION) \ - --build-arg=qemu_version=$(QEMU_VERSION) \ - -t "lifesensor/qemu:$(QEMU_VERSION)" \ - qemu - -.PHONY: vscode-image -ALL += vscode-image -TARGET_vscode += vscode-image -HELP_vscode-image = buils docker vscode image -vscode-image: qemu-image | check-docker - docker build \ - --build-arg=qemu_version=$(QEMU_VERSION) \ - -t "lifesensor/vscode:latest" \ - vscode - -### docker run targets ### - -.PHONY: idf -TARGET += idf -HELP_idf = runs idf container -idf: | idf-image check-docker - docker run \ - --rm \ - -ti \ - $(DOCKER_DEV) \ - -v idf-home:/home/developer:rw \ - -v "$(shell pwd)/..:$(shell pwd)/..:rw" \ - -w "$(WORKDIR)" \ - --hostname idf \ - $(DOCKEROPTS) \ - lifesensor/idf:$(IDF_VERSION) \ - /bin/bash -ic '$(EXEC)' - -.PHONY: qemu -TARGET += qemu -HELP_qemu = runs qemu container -qemu: | qemu-image check-docker - docker run \ - --rm \ - -ti \ - $(DOCKER_DEV) \ - -v qemu-home:/home/developer:rw \ - -v "$(shell pwd)/..:$(shell pwd)/..:rw" \ - -w "$(WORKDIR)" \ - --hostname qemu \ - $(DOCKEROPTS) \ - lifesensor/qemu:$(QEMU_VERSION) \ - /bin/bash -ic '$(EXEC)' - -.PHONY: code vscode -TARGET += vscode -HELP_vscode = runs vscode container -TARGET += code -HELP_code = runs vscode container -code vscode: | vscode-image check-docker - xhost local:root - docker run \ - --rm \ - -ti \ - $(DOCKER_DEV) \ - -v vscode-home:/home/developer:rw \ - -v /tmp/.X11-unix/:/tmp/.X11-unix \ - -e DISPLAY=$(DISPLAY) \ - -e LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0 \ - -v "$(shell pwd)/..:$(shell pwd)/..:rw" \ - -w "$(WORKDIR)" \ - --shm-size=512m \ - --hostname vscode \ - $(DOCKEROPTS) \ - lifesensor/vscode:latest \ - /bin/bash -ic '$(EXEC)' - -### docker remove volume targets ### - -.PHONY: clean-idf -CLEAN += clean-idf -HELP_clean-idf = removes idf volume -clean-idf: - -docker volume rm idf-home - -.PHONY: clean-qemu -CLEAN += clean-qemu -HELP_clean-qemu = removes qemu volume -clean-qemu: - -docker volume rm qemu-home - -.PHONY: clean-vscode -CLEAN += clean-vscode -HELP_clean-vscode = removes vscode volume -clean-vscode: - -docker volume rm vscode-home - -### docker remove image targets ### - -.PHONY: distclean-idf -DISTCLEAN += distclean-idf -HELP_distclean-idf = removes idf docker image -distclean-idf: clean-idf - -docker image remove "lifesensor/idf:$(IDF_VERSION)" - docker image prune - -.PHONY: distclean-qemu -DISTCLEAN += distclean-qemu -HELP_distclean-qemu = removes qemu docker image -distclean-qemu: clean-qemu - -docker image remove "lifesensor/qemu:$(QEMU_VERSION)" - docker image prune - -.PHONY: distclean-vscode -DISTCLEAN += distclean-vscode -HELP_distclean-vscode = removes vscode docker image -distclean-vscode: clean-vscode - -docker image remove "lifesensor/vscode:latest" - docker image prune - -### docker check installation targets ### - -.PHONY: check-docker -CHECK += check-docker -check-docker: check-docker-installed check-docker-group - -.PHONY: check-docker-installed -check-docker-installed: - @1>&2 echo -n "checking if docker is installed..." -ifeq (,$(shell which docker 2> /dev/null)) - @1>&2 echo - @1>&2 echo "########################################" - @1>&2 echo "# DOCKER DOES NOT SEEM TO BE INSTALLED #" - @1>&2 echo "########################################" - @1>&2 echo "the docker binary could not be found in your PATH!" - @1>&2 echo "This usally means docker is simply not installed." - @1>&2 echo -ifneq (,$(shell grep -i debian /etc/*-release)) - @1>&2 echo "I detected your platform as DEBIAN, so please run" - @1>&2 echo - @1>&2 make -sn install-docker-debian - @1>&2 echo - @1>&2 echo OR simply - @1>&2 echo - @1>&2 echo make install-docker-debian - @1>&2 echo -else ifneq (,$(shell grep -i ubuntu /etc/*-release)) - @1>&2 echo "I detected your platform as UBUNTU, so please run" - @1>&2 echo - @1>&2 make -sn install-docker-ubuntu - @1>&2 echo - @1>&2 echo OR simply - @1>&2 echo - @1>&2 echo make install-docker-ubuntu - @1>&2 echo -else ifneq (,$(shell grep -i "arch linux" /etc/*-release)) - @1>&2 echo "I detected your platform as ARCHLINUX, so please run" - @1>&2 echo - @1>&2 make -sn install-docker-archlinux - @1>&2 echo - @1>&2 echo OR simply - @1>&2 echo - @1>&2 echo make install-docker-archlinux - @1>&2 echo -else ifneq (,$(shell grep -i centos /etc/*-release)) - @1>&2 echo "I detected your platform as CENTOS, so please run" - @1>&2 echo - @1>&2 make -sn install-docker-centos - @1>&2 echo - @1>&2 echo OR simply - @1>&2 echo - @1>&2 echo make install-docker-centos - @1>&2 echo -else ifneq (,$(shell grep -i fedora /etc/*-release)) - @1>&2 echo "I detected your platform as FEDORA, so please run" - @1>&2 echo - @1>&2 make -sn install-docker-fedora - @1>&2 echo - @1>&2 echo OR simply - @1>&2 echo - @1>&2 echo make install-docker-fedora - @1>&2 echo -else - @1>&2 echo "I detected your platform as UNKNOWN," - @1>&2 echo - @1>&2 echo please run your package manager to install docker -endif - @exit 1 -endif - @1>&2 echo "SUCCESS" - - -### docker install targets ### - -.PHONY: install-docker-debian -install-docker-debian: - sudo apt-get update - sudo apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common - curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add - - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $$(lsb_release -cs) stable" - sudo apt-get update - sudo apt-get install docker-ce docker-ce-cli containerd.io - -.PHONY: install-docker-ubuntu -install-docker-ubuntu: - sudo apt-get update - sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common - 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 install docker-ce docker-ce-cli containerd.io - sudo apt-get update - -.PHONY: install-docker-archlinux -install-docker-archlinux: - sudo pacman -Sy docker - -.PHONY: install-docker-centos -install-docker-centos: - sudo yum install -y yum-utils device-mapper-persistent-data lvm2 - sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo - sudo yum install docker-ce docker-ce-cli containerd.io - -.PHONY: install-docker-fedora -install-docker-fedora: - sudo dnf -y install dnf-plugins-core - sudo dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo - sudo dnf install docker-ce docker-ce-cli containerd.io - -### docker check group target ### - -.PHONY: check-docker-group -check-docker-group: - @1>&2 echo -n "checking if user is in docker group..." -ifneq (,$(shell groups | grep -q docker)) - @1>&2 echo - @1>&2 echo "##############################################" - @1>&2 echo "# YOUR USER IS NOT PART OF THE DOCKER GROUP! #" - @1>&2 echo "##############################################" - @1>&2 echo "This means you need to execute every docker command with sudo" - @1>&2 echo "To avoid this, please add your user ($$USER) to the docker group with the following command:" - @1>&2 echo - @1>&2 echo "sudo usermod -a -G docker $$USER" - @1>&2 echo - @exit 1 -endif - @1>&2 echo "SUCCESS" - -include ../.Makefile.template diff --git a/docker.old/README.md b/docker.old/README.md deleted file mode 100644 index c7f3e83..0000000 --- a/docker.old/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Docker - -This directory contains the Dockerfiles for the containers used in this project. - -The images are build and run by make. -Run `make help` for more information. - -## idf - -- used to build the src and to create the resulting image - -## Qemu - -- used to simulate the esp32 core and test the image - -## Vscode - -- used to run a standardized IDE out of the box diff --git a/docker.old/idf/Dockerfile b/docker.old/idf/Dockerfile deleted file mode 100644 index d1d1e85..0000000 --- a/docker.old/idf/Dockerfile +++ /dev/null @@ -1,47 +0,0 @@ -FROM ubuntu:18.04 -RUN apt-get update && apt-get -y install \ -bison \ -ccache \ -cmake \ -flex \ -git \ -gperf \ -libffi-dev \ -libncurses5-dev \ -libssl-dev \ -libusb-1.0-0 \ -ninja-build \ -python \ -python-pip \ -python-setuptools \ -python3 \ -python3-pip \ -python3-setuptools \ -sudo \ -wget - -RUN update-alternatives --install /usr/bin/python python /usr/bin/python3 10 - -ARG UID=1000 -ARG GID=1000 -RUN groupadd -g $GID -o developer -RUN useradd -m -u $UID -g $GID -s /bin/bash developer -RUN echo "developer ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers - -WORKDIR /home/developer -USER developer -ENV LC_ALL=C.UTF-8 LANG=C.UTF-8 - -ARG idf_version -RUN sudo git clone --depth 1 -b ${idf_version} \ ---recursive https://github.com/espressif/esp-idf.git /opt/esp-idf -WORKDIR /opt/esp-idf -COPY idf_tools.py.patch idf_tools.py.patch -RUN sudo git apply idf_tools.py.patch -ENV IDF_TOOLS_PATH=/opt/espressif -RUN sudo -HE python tools/idf_tools.py --non-interactive install \ -&& sudo rm -rf ${IDF_TOOLS_PATH}/dist -RUN sudo -HE python tools/idf_tools.py --non-interactive install-python-env -RUN python tools/idf_tools.py export >> ~/.bashrc - -WORKDIR /home/developer diff --git a/docker.old/platformio/Dockerfile b/docker.old/platformio/Dockerfile deleted file mode 100644 index 2d9250a..0000000 --- a/docker.old/platformio/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM ubuntu:18.04 - -RUN apt-get update -RUN apt-get -y install python3 python3-pip git sudo - -ARG UID=1000 -ARG GID=1000 -RUN groupadd -g $GID -o developer -RUN useradd -m -u $UID -g $GID -s /bin/bash developer -RUN echo "developer ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers - -WORKDIR /home/developer -USER developer -ENV LC_ALL=C.UTF-8 LANG=C.UTF-8 -ENV PATH="${PATH}:/home/developer/.local/bin" -ENV PATH="${PATH}:/home/developer/.platformio/packages/toolchain-xtensa32/bin/" - -ARG platformio_version -RUN pip3 install --no-cache --upgrade pip setuptools wheel platformio==${platformio_version} diff --git a/docker.old/qemu/Dockerfile b/docker.old/qemu/Dockerfile deleted file mode 100644 index 438b34c..0000000 --- a/docker.old/qemu/Dockerfile +++ /dev/null @@ -1,45 +0,0 @@ -ARG idf_version -FROM lifesensor/idf:${idf_version} - -RUN sudo apt-get -y install \ -bison \ -curl \ -flex \ -gettext \ -libcairo2-dev \ -libffi-dev \ -libglib2.0-dev \ -libgsl-dev \ -libreadline-dev \ -libxml2-dev \ -pkg-config \ -zlib1g-dev - -RUN curl -L \ -http://ftp.gnome.org/pub/gnome/sources/glib/2.48/glib-2.48.0.tar.xz \ -| unxz | tar -xC /tmp \ -&& cd /tmp/glib-2.48.0/ \ -&& ./configure \ -&& make -j $(grep -ci processor /proc/cpuinfo) \ -&& sudo make install \ -&& rm -rf /tmp/glib-2.48.0 - -ARG qemu_version -RUN git clone --depth 1 -b ${qemu_version} \ ---recursive https://github.com/espressif/qemu.git /tmp/qemu \ -&& mkdir -p /tmp/qemu/build \ -&& cd /tmp/qemu/build \ -&& ../configure \ ---target-list=xtensa-softmmu \ ---enable-debug \ ---enable-sanitizers \ ---disable-strip \ ---disable-user \ ---disable-capstone \ ---disable-vnc \ ---disable-sdl \ ---disable-gtk \ -&& make -j $(grep -ci processor /proc/cpuinfo) \ -&& sudo make install \ -&& rm -rf /tmp/qemu - diff --git a/docker.old/vscode/Dockerfile b/docker.old/vscode/Dockerfile deleted file mode 100644 index 830804c..0000000 --- a/docker.old/vscode/Dockerfile +++ /dev/null @@ -1,25 +0,0 @@ -ARG qemu_version -FROM lifesensor/qemu:${qemu_version} - -RUN sudo apt-get -y install libx11-xcb1 libasound2 -RUN \ - curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /usr/share/keyrings/packages.microsoft.gpg > /dev/null&& \ - sudo chmod 664 /usr/share/keyrings/packages.microsoft.gpg && \ - sudo chown root:root /usr/share/keyrings/packages.microsoft.gpg && \ - echo "deb [arch=amd64 signed-by=/usr/share/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/vscode stable main" | sudo tee /etc/apt/sources.list.d/vscode.list > /dev/null && \ - sudo apt-get -y install apt-transport-https && \ - sudo apt-get update && \ - sudo apt-get -y install code - -RUN LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0 code -w --install-extension espressif.esp-idf-extension -RUN bash -ic 'find ~/.vscode -name requirements.txt -exec sudo -H $IDF_PYTHON_ENV_PATH/bin/pip install -r "{}" ";"' -RUN mkdir -p /home/developer/.config/Code/User/ \ -&& bash -ic 'echo "{ \ - \"telemetry.enableCrashReporter\": false, \ - \"telemetry.enableTelemetry\": false, \ - \"update.mode\": "none", \ - \"idf.pythonSystemBinPath\": \""$IDF_PYTHON_ENV_PATH/bin/python"\", \ - \"idf.espIdfPath\": \"/opt/esp-idf\", \ - \"idf.customExtraPaths\": \"\", \ - \"idf.showOnboardingOnInit\": false \ -}"' > /home/developer/.config/Code/User/settings.json diff --git a/docker/.template/Makefile b/docker/.template/Makefile new file mode 100644 index 0000000..f03dffd --- /dev/null +++ b/docker/.template/Makefile @@ -0,0 +1 @@ +include $(shell git rev-parse --show-toplevel)/.make/subdirs.mk \ No newline at end of file diff --git a/docker/.template/README.md b/docker/.template/README.md new file mode 100644 index 0000000..6a084e5 --- /dev/null +++ b/docker/.template/README.md @@ -0,0 +1,38 @@ + +# Docker image template + + +This template should be used to create a new image. +1. Copy this template directory +2. Give your new image a meaningful name. +3. Append your new image to the image list in [../README.md](../README.md) +4. Edit this README + - Change title! +5. Continue editing in [v1/README.md](./1/README.md) + +--- +## Features + +- provides template for docker image + +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build all versions +- `make build` + - build all versions +- `make clean` + - delete volumes of all versions +- `make distclean` + - delete images of all version + +--- +## Versions + + + +- [v1](./v1/) + - initial version \ No newline at end of file diff --git a/docker/.template/v1/Dockerfile b/docker/.template/v1/Dockerfile new file mode 100644 index 0000000..419b768 --- /dev/null +++ b/docker/.template/v1/Dockerfile @@ -0,0 +1,26 @@ +FROM lifesensor/base:latest + +ARG FOO +ARG BAR + +########################### +# install needed packages # +########################### +RUN sudo -HE apt-get install -y \ +g++ \ +cmake \ +python3 \ +; + +############################################################### +# build & install some software # +# please build & install & remove in a single RUN instruction # +# to avoid intermediate images # +############################################################### +RUN export REPO=/tmp/BAR \ + && git clone https://github.com/FOO/BAR.git $REPO \ + && mkdir $REPO/build \ + && cd $REPO/build \ + && cmake .. \ + && sudo make install \ + && sudo rm -rf $REPO \ No newline at end of file diff --git a/docker/.template/v1/Makefile b/docker/.template/v1/Makefile new file mode 100644 index 0000000..7f532be --- /dev/null +++ b/docker/.template/v1/Makefile @@ -0,0 +1,25 @@ +### DOCKER IMAGE DEPENDENCY ### +FROM := base/v1 + +### DOCKER IMAGE ARGUMENTS ### +ARGS += FOO +HELP_FOO := modifies something +FOO := default_value + +ARGS += BAR +HELP_BAR := modifies something else +BAR := default_value + +### DOCKER BUILD OPTIONS ### +TAG := latest +BUILD := #empty + +### DOCKER RUN OPTIONS ### +RUN := #empty +VOLUMES := #empty +WORKDIR := $(shell pwd) +ENVS := #empty +EXEC := #empty +MOUNT := $(shell pwd) + +include $(shell git rev-parse --show-toplevel)/.make/docker.mk \ No newline at end of file diff --git a/docker/.template/v1/README.md b/docker/.template/v1/README.md new file mode 100644 index 0000000..9c5769b --- /dev/null +++ b/docker/.template/v1/README.md @@ -0,0 +1,45 @@ + +# Docker image template (version v1) + + +This template should be used to create a new docker image version. +1. Copy this template directory +2. Give your new image a meaningful name. +3. Append your new version to the version list in [../README.md](../README.md) +4. Edit this README + - Change title & version! +5. Edit the [Dockerfile](./Dockerfile) +6. Edit the [Makefile](./Makefile) + +--- +## Features + +- installs `software one` +- installs `software two` + +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build image & run container +- `make build` + - build image +- `make run` + - run container +- `make clean` + - delete volumes +- `make distclean` + - delete image + +--- +## Image arguments + +- `FOO` + - modifies something +- `BAR` + - modifies something else + +--- +See the [Dockerfile](./Dockerfile) or [Makefile](./Makefile) for more information diff --git a/docker/Makefile b/docker/Makefile new file mode 100644 index 0000000..7f48ff4 --- /dev/null +++ b/docker/Makefile @@ -0,0 +1,2 @@ +DEPENDS = $(dir $(wildcard */Makefile)) +include $(shell git rev-parse --show-toplevel)/.make/common.mk \ No newline at end of file diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..c18a8cf --- /dev/null +++ b/docker/README.md @@ -0,0 +1,74 @@ +# Docker + +This directory contains Dockerfiles for the containers used in this project. + +--- +## List of images +- [base](./base/) + - base image for all other images +- [idf](./idf/) + - idf framework for the esp32 firmware +- [qemu](./qemu/) + - qemu for architecture simulation + +--- +## Makefile + +- `make help` + - show Makefile options +- `make build` + - build all versions of all images +- `make clean` + - delete all volumes of all versions of all images +- `make distclean` + - delete all all versions of all images + +--- +## Run a container +1. change into the version directory of the image +2. run `make run` + - you may specify additional options, see `make help` for more information + - you may specify `EXEC` to run a single command + - i.e. `make run EXEC='echo hello world'` + +--- +## Adding new images +- If you want to add a new image, +please copy and modify the [Template](./.template/) + +--- +## Structure +``` +. +├── README.md : mandatory README.md you are currently reading +├── Makefile : mandatory Makefile executing targets in all subdirs +│ +├── : mandatory unique name of the image +│   ├── README.md : mandatory general description of the image +│   │ and elaboration of differences between version +│ ├── Makefile : mandatory Makefile executing targets in all subdirs +│   │ +│   ├── : mandatory unique image version +│   │   ├── README.md : mandatory version specific description of the image, +│   │   │   should list all features +│   │ ├── Makefile : mandatory Makefile for integration of the image +│   │ ├── Dockerfile : mandatory Dockerfile to build image +│   │ └── ... +│   │ +│   ├── +│   │   └── ... +│   └── ... +│ +├── +│   └── ... +└── ... +``` + +--- +## Rules +- *Docker images* of the LifeSensor project should have a dedicated directory here. +- *Docker images* can be based on each other (see [base](./base/) image). +- *Docker images* may compete for the same functionality. + - Different [*parts*](../parts/) may use different *docker images* +- *Docker images* should follow the directory scheme defined by the [*template image*](./.template/) + - *Docker images* may contain any additional files & directories diff --git a/docker/base/Makefile b/docker/base/Makefile new file mode 100644 index 0000000..7f48ff4 --- /dev/null +++ b/docker/base/Makefile @@ -0,0 +1,2 @@ +DEPENDS = $(dir $(wildcard */Makefile)) +include $(shell git rev-parse --show-toplevel)/.make/common.mk \ No newline at end of file diff --git a/docker/base/README.md b/docker/base/README.md new file mode 100644 index 0000000..56a1e4d --- /dev/null +++ b/docker/base/README.md @@ -0,0 +1,32 @@ + +# Docker image base + + +This is the base image of all other images. + +--- +## Features + +- setup of user `developer` + +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build all versions +- `make build` + - build all versions +- `make clean` + - delete volumes of all versions +- `make distclean` + - delete images of all version + +--- +## Versions + + + +- [v1](./v1/) + - initial version \ No newline at end of file diff --git a/docker/base/v1/Dockerfile b/docker/base/v1/Dockerfile new file mode 100644 index 0000000..4884971 --- /dev/null +++ b/docker/base/v1/Dockerfile @@ -0,0 +1,16 @@ +FROM ubuntu:18.04 +RUN apt-get update && apt-get -y install \ +git \ +make \ +sudo \ +; + +# creates user 'developer' +ARG UID=1000 +ARG GID=1000 +RUN groupadd -g $GID -o developer \ + && useradd -m -u $UID -g $GID -s /bin/bash developer \ + && echo "developer ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers +WORKDIR /home/developer +USER developer +ENV LC_ALL=C.UTF-8 LANG=C.UTF-8 DEBIAN_FRONTEND=noninteractive diff --git a/docker/base/v1/Makefile b/docker/base/v1/Makefile new file mode 100644 index 0000000..ca26c3b --- /dev/null +++ b/docker/base/v1/Makefile @@ -0,0 +1,25 @@ +### DOCKER IMAGE DEPENDENCY ### +FROM := #empty + +### DOCKER IMAGE ARGUMENTS ### +ARGS += UID +HELP_UID := user id of developer +UID := $(shell id -u) + +ARGS += GID +HELP_GID := group id of developer +GID := $(shell id -g) + +### DOCKER BUILD OPTIONS ### +TAG := latest +BUILD := #empty + +### DOCKER RUN OPTIONS ### +RUN := #empty +VOLUMES := #empty +WORKDIR := $(shell pwd) +ENVS := #empty +EXEC := #empty +MOUNT := $(shell pwd) + +include $(shell git rev-parse --show-toplevel)/.make/docker.mk \ No newline at end of file diff --git a/docker/base/v1/README.md b/docker/base/v1/README.md new file mode 100644 index 0000000..932ecef --- /dev/null +++ b/docker/base/v1/README.md @@ -0,0 +1,38 @@ + +# Docker image base (version v1) + + +This is the base image of all other images. + +## Features + +- installs `sudo`, `make`, `git` +- creates user `developer` +- adds `developer` to sudoers + +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build image & run container +- `make build` + - build image +- `make run` + - run container +- `make clean` + - delete volumes +- `make distclean` + - delete image + +--- +## Image arguments + +- `UID` + - sets uid of `developer` +- `GID` + - sets gid of `developer` + +--- +See the [Dockerfile](./Dockerfile) or [Makefile](./Makefile) for more information diff --git a/docker/idf/Makefile b/docker/idf/Makefile new file mode 100644 index 0000000..7f48ff4 --- /dev/null +++ b/docker/idf/Makefile @@ -0,0 +1,2 @@ +DEPENDS = $(dir $(wildcard */Makefile)) +include $(shell git rev-parse --show-toplevel)/.make/common.mk \ No newline at end of file diff --git a/docker/idf/README.md b/docker/idf/README.md new file mode 100644 index 0000000..4de25bc --- /dev/null +++ b/docker/idf/README.md @@ -0,0 +1,32 @@ + +# Docker image idf + + +This is the idf image used to build the esp32 firmware. + +--- +## Features + +- installs [idf](https://github.com/espressif/esp-idf) & dependencies + +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build all versions +- `make build` + - build all versions +- `make clean` + - delete volumes of all versions +- `make distclean` + - delete images of all version + +--- +## Versions + + + +- [v4.0](./v4.0/) + - idf version v4.0 \ No newline at end of file diff --git a/docker/idf/v4.0/Dockerfile b/docker/idf/v4.0/Dockerfile new file mode 100644 index 0000000..be4f4f1 --- /dev/null +++ b/docker/idf/v4.0/Dockerfile @@ -0,0 +1,38 @@ +FROM lifesensor/base/v1:latest + +RUN sudo -HE apt-get -y install \ +bison \ +ccache \ +cmake \ +flex \ +git \ +gperf \ +libffi-dev \ +libncurses5-dev \ +libssl-dev \ +libusb-1.0-0 \ +ninja-build \ +python \ +python-pip \ +python-setuptools \ +python3 \ +python3-pip \ +python3-setuptools \ +wget \ +; + +RUN sudo -HE update-alternatives --install /usr/bin/python python /usr/bin/python3 10 + +ARG IDF_VERSION +RUN sudo git clone --depth 1 -b ${IDF_VERSION} \ +--recursive https://github.com/espressif/esp-idf.git /opt/esp-idf +COPY idf_tools.py.patch /opt/esp-idf/idf_tools.py.patch +ENV IDF_TOOLS_PATH=/opt/espressif IDF_PATH=/opt/esp-idf/ +RUN sudo git -C ${IDF_PATH} apply idf_tools.py.patch \ + && sudo -HE python ${IDF_PATH}/tools/idf_tools.py --non-interactive install \ + && sudo rm -rf ${IDF_TOOLS_PATH}/dist \ + && sudo -HE python ${IDF_PATH}/tools/idf_tools.py --non-interactive install-python-env \ + && python ${IDF_PATH}/tools/idf_tools.py export >> ~/.bashrc \ +; +RUN echo '[ -n "$IDF_PORT" ] && sudo chgrp developer $IDF_PORT' >> ~/.bashrc +RUN mkdir ~/.ccache \ No newline at end of file diff --git a/docker/idf/v4.0/Makefile b/docker/idf/v4.0/Makefile new file mode 100644 index 0000000..bc9c100 --- /dev/null +++ b/docker/idf/v4.0/Makefile @@ -0,0 +1,20 @@ +DEPENDS := ../../base/v1 + +### DOCKER IMAGE ARGUMENTS ### +ARGS += IDF_VERSION +HELP_IDF_VERSION := idf version to use +IDF_VERSION := v4.0 + +### DOCKER BUILD OPTIONS ### +TAG := latest +BUILD := #empty + +### DOCKER RUN OPTIONS ### +RUN := #empty +VOLUMES += lifesensor-idf-v4.0-home-ccache:/home/developer/.ccache:rw +WORKDIR := $(shell pwd) +ENVS := #empty +EXEC := #empty +MOUNT := $(shell pwd) + +include $(shell git rev-parse --show-toplevel)/.make/docker.mk \ No newline at end of file diff --git a/docker/idf/v4.0/README.md b/docker/idf/v4.0/README.md new file mode 100644 index 0000000..60a9983 --- /dev/null +++ b/docker/idf/v4.0/README.md @@ -0,0 +1,35 @@ + +# Docker image idf (version v4.0) + + +This is the idf image used to build the esp32 firmware. + +--- +## Features + +- installs [idf](https://github.com/espressif/esp-idf) version v4.0 & dependencies + +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build image & run container +- `make build` + - build image +- `make run` + - run container +- `make clean` + - delete volumes +- `make distclean` + - delete image + +---- +## Image arguments + +- `IDF_VERSION` + - idf version to use + +--- +See the [Dockerfile](./Dockerfile) or [Makefile](./Makefile) for more information \ No newline at end of file diff --git a/docker.old/idf/idf_tools.py.patch b/docker/idf/v4.0/idf_tools.py.patch similarity index 100% rename from docker.old/idf/idf_tools.py.patch rename to docker/idf/v4.0/idf_tools.py.patch diff --git a/docker/qemu/Makefile b/docker/qemu/Makefile new file mode 100644 index 0000000..7f48ff4 --- /dev/null +++ b/docker/qemu/Makefile @@ -0,0 +1,2 @@ +DEPENDS = $(dir $(wildcard */Makefile)) +include $(shell git rev-parse --show-toplevel)/.make/common.mk \ No newline at end of file diff --git a/docker/qemu/README.md b/docker/qemu/README.md new file mode 100644 index 0000000..271d32c --- /dev/null +++ b/docker/qemu/README.md @@ -0,0 +1,30 @@ + +# Docker image qemu + + +This is the qemu image used to simulate various architectures. + +--- +## Features + +- installs qemu + +--- +## Makefile + +- `make help` + - show Makefile options +- `make build` + - build all versions +- `make clean` + - delete volumes of all versions +- `make distclean` + - delete images of all version + +--- +## Versions + + + +- [esp-develop](./esp-develop/) + - qemu fork for xtensa (esp32) architecture \ No newline at end of file diff --git a/docker/qemu/esp-develop/Dockerfile b/docker/qemu/esp-develop/Dockerfile new file mode 100644 index 0000000..5e7db4c --- /dev/null +++ b/docker/qemu/esp-develop/Dockerfile @@ -0,0 +1,49 @@ +ARG IDF_VERSION +FROM lifesensor/idf/${IDF_VERSION}:latest + +RUN sudo -HE apt-get -y install \ +bison \ +curl \ +flex \ +gettext \ +git \ +libcairo2-dev \ +libffi-dev \ +libglib2.0-dev \ +libgsl-dev \ +libreadline-dev \ +libxml2-dev \ +pkg-config \ +zlib1g-dev \ +; + +RUN curl -L \ + http://ftp.gnome.org/pub/gnome/sources/glib/2.48/glib-2.48.0.tar.xz \ + | unxz | tar -xC /tmp \ + && cd /tmp/glib-2.48.0/ \ + && ./configure \ + && make -j $(grep -ci processor /proc/cpuinfo) \ + && sudo make install \ + && rm -rf /tmp/glib-2.48.0 \ +; + +ARG QEMU_VERSION +RUN git clone --depth 1 -b ${QEMU_VERSION} \ + https://github.com/espressif/qemu.git /tmp/qemu \ + && mkdir -p /tmp/qemu/build \ + && cd /tmp/qemu/build \ + && ../configure \ + --target-list=xtensa-softmmu \ + --enable-debug \ + --enable-sanitizers \ + --disable-strip \ + --disable-user \ + --disable-capstone \ + --disable-vnc \ + --disable-sdl \ + --disable-gtk \ + && make -j $(grep -ci processor /proc/cpuinfo) \ + && sudo make install \ + && rm -rf /tmp/qemu \ +; + diff --git a/docker/qemu/esp-develop/Makefile b/docker/qemu/esp-develop/Makefile new file mode 100644 index 0000000..5e05de1 --- /dev/null +++ b/docker/qemu/esp-develop/Makefile @@ -0,0 +1,23 @@ +DEPENDS := ../../idf/v4.0 + +### DOCKER IMAGE ARGUMENTS ### +ARGS += QEMU_VERSION +HELP_QEMU_VERSION := qemu version to use +QEMU_VERSION := esp-develop + +ARGS += IDF_VERSION +IDF_VERSION := v4.0 + +### DOCKER BUILD OPTIONS ### +TAG := latest +BUILD := #empty + +### DOCKER RUN OPTIONS ### +RUN := #empty +VOLUMES := lifesensor-qemu-esp-develop-home:/home/developer:rw +WORKDIR := $(shell pwd) +ENVS := #empty +EXEC := #empty +MOUNT := $(shell pwd) + +include $(shell git rev-parse --show-toplevel)/.make/docker.mk \ No newline at end of file diff --git a/docker/qemu/esp-develop/README.md b/docker/qemu/esp-develop/README.md new file mode 100644 index 0000000..9d9d9cc --- /dev/null +++ b/docker/qemu/esp-develop/README.md @@ -0,0 +1,35 @@ + +# Docker image qemu (version esp-develop) + + +This is the qemu image used to simulate the esp32. + +--- +## Features + +- installs xtensa-architecture capable [qemu](https://github.com/espressif/qemu) & dependencies + +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build image & run container +- `make build` + - build image +- `make run` + - run container +- `make clean` + - delete volumes +- `make distclean` + - delete image + +--- +## Image arguments + +- `QEMU_VERSION` + - qemu version to use + +--- +See the [Dockerfile](./Dockerfile) or [Makefile](./Makefile) for more information \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 34a9f95..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -esp32_overview.svg : esp32_overview.gv - neato -Tsvg $^ > $@ diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..7b2e621 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,6 @@ +# LifeSensor documents + +## Documents + +- [lifesensor_logo.png](./lifesensor_logo.png) + - official logo of the [*LifeSensor*](https://lifesensor.org) project \ No newline at end of file diff --git a/docs/lifesensor_logo.png b/docs/lifesensor_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..85ef3add8c47471cdfad889ec9b691de70654306 GIT binary patch literal 174376 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%0A!sov(@I5hrk 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 --> +# Part template + + +This template should be used to create a new part. +1. Copy this template directory +2. Give your new part a meaningful name. +3. Append your new part to the part list in [../README.md](../README.md) +4. Edit this README + - Change title! +5. Continue editing in [v1/README.md](./1/README.md) + +--- +## Features + +- provides template for part + +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build & test all versions +- `make build` + - build all versions +- `make test` + - test all versions +- `make clean` + - clean all versions +- `make distclean` + - distclean all version + +--- +## Versions + + + +- [v1](./v1/) + - initial version \ No newline at end of file diff --git a/parts/.template/v1/Makefile b/parts/.template/v1/Makefile new file mode 100644 index 0000000..020dacd --- /dev/null +++ b/parts/.template/v1/Makefile @@ -0,0 +1 @@ +include $(shell git rev-parse --show-toplevel)/.make/part.mk \ No newline at end of file diff --git a/parts/.template/v1/README.md b/parts/.template/v1/README.md new file mode 100644 index 0000000..d5be71e --- /dev/null +++ b/parts/.template/v1/README.md @@ -0,0 +1,71 @@ + +# Part template (version v1) + + +This template should be used to create a new part version. +1. Edit this README + - Change title & version! +2. Delete unneeded directories +3. Edit READMEs of directories +4. Edit Makefile + +--- +## Features + +- provides template for part version + +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build & test everything +- `make build` + - build everything +- `make test` + - test everything +- `make clean` + - clean everything +- `make distclean` + - distclean everything + +---- +## [Code](./code/) + + + +- template code directory + +---- +## [Docs](./docs/) + + +- template docs directory + +--- +## [Kicad](./kicad/) + + +- template kicad directory + +--- +## [Mechanics](./mechanics/) + + +- template machanics directory + +--- +## [Requirements](./requirements/) + + +- template requirements directory + +--- +## [Scripts](./scripts/) + + +- template scripts directory + +--- +See the other READMEs or [Makefile](./Makefile) for more information \ No newline at end of file diff --git a/parts/.template/v1/code/.gitignore b/parts/.template/v1/code/.gitignore new file mode 100644 index 0000000..cc2dd92 --- /dev/null +++ b/parts/.template/v1/code/.gitignore @@ -0,0 +1 @@ +firmware \ No newline at end of file diff --git a/parts/.template/v1/code/Makefile b/parts/.template/v1/code/Makefile new file mode 100644 index 0000000..4ba67a3 --- /dev/null +++ b/parts/.template/v1/code/Makefile @@ -0,0 +1,17 @@ + +TARGET_default := all +TARGET_all := build-firmware + +firmware: main.c + gcc -o $@ $< + +.phony: build-firmware +TARGET_build += build-firmware +build-firmware: firmware + +.phony: clean-firmware +TARGET_clean += clean-firmware +clean-firmware: + rm -f firmware + +include $(shell git rev-parse --show-toplevel)/.make/common.mk \ No newline at end of file diff --git a/parts/.template/v1/code/README.md b/parts/.template/v1/code/README.md new file mode 100644 index 0000000..0b04bbe --- /dev/null +++ b/parts/.template/v1/code/README.md @@ -0,0 +1,14 @@ + +# Part template code (version v1) + + +This the firmware implementation of the template. +1. Edit this README + - Change title & version! + +--- +## Features + +- hello world application +- Makefile building the application + diff --git a/parts/.template/v1/code/main.c b/parts/.template/v1/code/main.c new file mode 100644 index 0000000..9c85873 --- /dev/null +++ b/parts/.template/v1/code/main.c @@ -0,0 +1,5 @@ +#include + +int main() { + printf("firmware!\n"); +} diff --git a/parts/.template/v1/docs/README.md b/parts/.template/v1/docs/README.md new file mode 100644 index 0000000..e886a1a --- /dev/null +++ b/parts/.template/v1/docs/README.md @@ -0,0 +1,12 @@ + +# Part template documents (version v1) + + +This the document collection of the part template. +1. Edit this README + - Change title & version! + +--- +## Documents + +- none \ No newline at end of file diff --git a/parts/.template/v1/kicad/README.md b/parts/.template/v1/kicad/README.md new file mode 100644 index 0000000..20a8d3d --- /dev/null +++ b/parts/.template/v1/kicad/README.md @@ -0,0 +1,15 @@ + +# Part template kicad (version v1) + + +These are the kicad files of the template. +1. Edit this README + - Change title & version! + +--- +## Projects + +- [voltage_divider](./voltage_divider/) + - simple voltage divider +- [simulation](./simulation/) + - simple simulation setup for the voltage divider \ No newline at end of file diff --git a/parts/.template/v1/kicad/simulation/simulation-cache.lib b/parts/.template/v1/kicad/simulation/simulation-cache.lib new file mode 100644 index 0000000..3400715 --- /dev/null +++ b/parts/.template/v1/kicad/simulation/simulation-cache.lib @@ -0,0 +1,54 @@ +EESchema-LIBRARY Version 2.4 +#encoding utf-8 +# +# Device_R +# +DEF Device_R R 0 0 N Y 1 F N +F0 "R" 80 0 50 V V C CNN +F1 "Device_R" 0 0 50 V V C CNN +F2 "" -70 0 50 V I C CNN +F3 "" 0 0 50 H I C CNN +$FPLIST + R_* +$ENDFPLIST +DRAW +S -40 -100 40 100 0 1 10 N +X ~ 1 0 150 50 D 50 50 1 1 P +X ~ 2 0 -150 50 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +# Simulation_SPICE_VSIN +# +DEF Simulation_SPICE_VSIN V 0 1 N Y 1 F N +F0 "V" 100 100 50 H V L CNN +F1 "Simulation_SPICE_VSIN" 100 0 50 H V L CNN +F2 "" 0 0 50 H I C CNN +F3 "" 0 0 50 H I C CNN +F4 "Y" 0 0 50 H I L CNN "Spice_Netlist_Enabled" +F5 "V" 0 0 50 H I L CNN "Spice_Primitive" +F6 "sin(0 1 1k)" 100 -100 50 H V L CNN "Spice_Model" +DRAW +A -25 0 25 1 1800 0 0 0 N 0 0 -50 0 +A 25 0 25 1800 -1 0 0 0 N 0 0 50 0 +C 0 0 100 0 1 10 f +T 0 0 75 50 0 0 0 + Normal 0 C C +X ~ 1 0 200 100 D 50 50 1 1 P +X ~ 2 0 -200 100 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +# power_GND +# +DEF power_GND #PWR 0 0 Y Y 1 F P +F0 "#PWR" 0 -250 50 H I C CNN +F1 "power_GND" 0 -150 50 H V C CNN +F2 "" 0 0 50 H I C CNN +F3 "" 0 0 50 H I C CNN +DRAW +P 6 0 1 0 0 0 0 -50 50 -50 0 -100 -50 -50 0 -50 N +X GND 1 0 0 0 D 50 50 1 1 W N +ENDDRAW +ENDDEF +# +#End Library diff --git a/parts/.template/v1/kicad/simulation/simulation.kicad_pcb b/parts/.template/v1/kicad/simulation/simulation.kicad_pcb new file mode 100644 index 0000000..02c8ecb --- /dev/null +++ b/parts/.template/v1/kicad/simulation/simulation.kicad_pcb @@ -0,0 +1 @@ +(kicad_pcb (version 4) (host kicad "dummy file") ) diff --git a/parts/.template/v1/kicad/simulation/simulation.pro b/parts/.template/v1/kicad/simulation/simulation.pro new file mode 100644 index 0000000..152769c --- /dev/null +++ b/parts/.template/v1/kicad/simulation/simulation.pro @@ -0,0 +1,33 @@ +update=22/05/2015 07:44:53 +version=1 +last_client=kicad +[general] +version=1 +RootSch= +BoardNm= +[pcbnew] +version=1 +LastNetListRead= +UseCmpFile=1 +PadDrill=0.600000000000 +PadDrillOvalY=0.600000000000 +PadSizeH=1.500000000000 +PadSizeV=1.500000000000 +PcbTextSizeV=1.500000000000 +PcbTextSizeH=1.500000000000 +PcbTextThickness=0.300000000000 +ModuleTextSizeV=1.000000000000 +ModuleTextSizeH=1.000000000000 +ModuleTextSizeThickness=0.150000000000 +SolderMaskClearance=0.000000000000 +SolderMaskMinWidth=0.000000000000 +DrawSegmentWidth=0.200000000000 +BoardOutlineThickness=0.100000000000 +ModuleOutlineThickness=0.150000000000 +[cvpcb] +version=1 +NetIExt=net +[eeschema] +version=1 +LibDir= +[eeschema/libraries] diff --git a/parts/.template/v1/kicad/simulation/simulation.sch b/parts/.template/v1/kicad/simulation/simulation.sch new file mode 100644 index 0000000..4dddb22 --- /dev/null +++ b/parts/.template/v1/kicad/simulation/simulation.sch @@ -0,0 +1,68 @@ +EESchema Schematic File Version 4 +EELAYER 30 0 +EELAYER END +$Descr A4 11693 8268 +encoding utf-8 +Sheet 1 2 +Title "" +Date "" +Rev "" +Comp "" +Comment1 "" +Comment2 "" +Comment3 "" +Comment4 "" +$EndDescr +$Sheet +S 3800 2700 950 700 +U 5EB613C9 +F0 "DUT" 50 +F1 "../voltage_divider/voltage_divider.sch" 50 +F2 "Vcc" U R 4750 2800 50 +F3 "Gnd" U R 4750 3300 50 +F4 "out" U R 4750 3050 50 +$EndSheet +$Comp +L Simulation_SPICE:VSIN V1 +U 1 1 5EB61A71 +P 5650 3050 +F 0 "V1" H 5780 3141 50 0000 L CNN +F 1 "VSIN" H 5780 3050 50 0000 L CNN +F 2 "" H 5650 3050 50 0001 C CNN +F 3 "~" H 5650 3050 50 0001 C CNN +F 4 "Y" H 5650 3050 50 0001 L CNN "Spice_Netlist_Enabled" +F 5 "V" H 5650 3050 50 0001 L CNN "Spice_Primitive" +F 6 "sin(0 1 1k)" H 5780 2959 50 0000 L CNN "Spice_Model" + 1 5650 3050 + 1 0 0 -1 +$EndComp +Wire Wire Line + 5650 2850 5650 2800 +Wire Wire Line + 5650 2800 4750 2800 +Wire Wire Line + 5650 3250 5650 3300 +Wire Wire Line + 5650 3300 4750 3300 +$Comp +L power:GND #PWR01 +U 1 1 5EB61DF2 +P 5650 3300 +F 0 "#PWR01" H 5650 3050 50 0001 C CNN +F 1 "GND" H 5655 3127 50 0000 C CNN +F 2 "" H 5650 3300 50 0001 C CNN +F 3 "" H 5650 3300 50 0001 C CNN + 1 5650 3300 + 1 0 0 -1 +$EndComp +Connection ~ 5650 3300 +Text Label 5200 3050 0 50 ~ 0 +out +Wire Wire Line + 5200 3050 4750 3050 +Text Label 5650 2700 1 50 ~ 0 +in +Wire Wire Line + 5650 2700 5650 2800 +Connection ~ 5650 2800 +$EndSCHEMATC diff --git a/parts/.template/v1/kicad/simulation/simulation.sch-bak b/parts/.template/v1/kicad/simulation/simulation.sch-bak new file mode 100644 index 0000000..fff8c68 --- /dev/null +++ b/parts/.template/v1/kicad/simulation/simulation.sch-bak @@ -0,0 +1,4 @@ +EESchema Schematic File Version 2 +EELAYER 25 0 +EELAYER END +$EndSCHEMATC diff --git a/parts/.template/v1/kicad/voltage_divider/voltage_divider-cache.lib b/parts/.template/v1/kicad/voltage_divider/voltage_divider-cache.lib new file mode 100644 index 0000000..478905e --- /dev/null +++ b/parts/.template/v1/kicad/voltage_divider/voltage_divider-cache.lib @@ -0,0 +1,21 @@ +EESchema-LIBRARY Version 2.4 +#encoding utf-8 +# +# Device_R +# +DEF Device_R R 0 0 N Y 1 F N +F0 "R" 80 0 50 V V C CNN +F1 "Device_R" 0 0 50 V V C CNN +F2 "" -70 0 50 V I C CNN +F3 "" 0 0 50 H I C CNN +$FPLIST + R_* +$ENDFPLIST +DRAW +S -40 -100 40 100 0 1 10 N +X ~ 1 0 150 50 D 50 50 1 1 P +X ~ 2 0 -150 50 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +#End Library diff --git a/parts/.template/v1/kicad/voltage_divider/voltage_divider.kicad_pcb b/parts/.template/v1/kicad/voltage_divider/voltage_divider.kicad_pcb new file mode 100644 index 0000000..02c8ecb --- /dev/null +++ b/parts/.template/v1/kicad/voltage_divider/voltage_divider.kicad_pcb @@ -0,0 +1 @@ +(kicad_pcb (version 4) (host kicad "dummy file") ) diff --git a/parts/.template/v1/kicad/voltage_divider/voltage_divider.pro b/parts/.template/v1/kicad/voltage_divider/voltage_divider.pro new file mode 100644 index 0000000..152769c --- /dev/null +++ b/parts/.template/v1/kicad/voltage_divider/voltage_divider.pro @@ -0,0 +1,33 @@ +update=22/05/2015 07:44:53 +version=1 +last_client=kicad +[general] +version=1 +RootSch= +BoardNm= +[pcbnew] +version=1 +LastNetListRead= +UseCmpFile=1 +PadDrill=0.600000000000 +PadDrillOvalY=0.600000000000 +PadSizeH=1.500000000000 +PadSizeV=1.500000000000 +PcbTextSizeV=1.500000000000 +PcbTextSizeH=1.500000000000 +PcbTextThickness=0.300000000000 +ModuleTextSizeV=1.000000000000 +ModuleTextSizeH=1.000000000000 +ModuleTextSizeThickness=0.150000000000 +SolderMaskClearance=0.000000000000 +SolderMaskMinWidth=0.000000000000 +DrawSegmentWidth=0.200000000000 +BoardOutlineThickness=0.100000000000 +ModuleOutlineThickness=0.150000000000 +[cvpcb] +version=1 +NetIExt=net +[eeschema] +version=1 +LibDir= +[eeschema/libraries] diff --git a/parts/.template/v1/kicad/voltage_divider/voltage_divider.sch b/parts/.template/v1/kicad/voltage_divider/voltage_divider.sch new file mode 100644 index 0000000..ca0296f --- /dev/null +++ b/parts/.template/v1/kicad/voltage_divider/voltage_divider.sch @@ -0,0 +1,55 @@ +EESchema Schematic File Version 4 +EELAYER 30 0 +EELAYER END +$Descr A4 11693 8268 +encoding utf-8 +Sheet 2 2 +Title "" +Date "" +Rev "" +Comp "" +Comment1 "" +Comment2 "" +Comment3 "" +Comment4 "" +$EndDescr +$Comp +L Device:R R1 +U 1 1 5EB5A4B7 +P 5500 3050 +F 0 "R1" H 5430 3004 50 0000 R CNN +F 1 "1k" H 5430 3095 50 0000 R CNN +F 2 "" V 5430 3050 50 0001 C CNN +F 3 "~" H 5500 3050 50 0001 C CNN + 1 5500 3050 + -1 0 0 1 +$EndComp +$Comp +L Device:R R2 +U 1 1 5EB5A7C0 +P 5500 3550 +F 0 "R2" H 5430 3504 50 0000 R CNN +F 1 "1k" H 5430 3595 50 0000 R CNN +F 2 "" V 5430 3550 50 0001 C CNN +F 3 "~" H 5500 3550 50 0001 C CNN + 1 5500 3550 + -1 0 0 1 +$EndComp +Wire Wire Line + 5500 2700 5500 2900 +Wire Wire Line + 5500 3200 5500 3300 +Wire Wire Line + 5500 3700 5500 3900 +Wire Wire Line + 5500 3300 5800 3300 +Connection ~ 5500 3300 +Wire Wire Line + 5500 3300 5500 3400 +Text HLabel 5500 2700 1 50 UnSpc ~ 0 +Vcc +Text HLabel 5500 3900 3 50 UnSpc ~ 0 +Gnd +Text HLabel 5800 3300 2 50 UnSpc ~ 0 +out +$EndSCHEMATC diff --git a/parts/.template/v1/kicad/voltage_divider/voltage_divider.sch-bak b/parts/.template/v1/kicad/voltage_divider/voltage_divider.sch-bak new file mode 100644 index 0000000..fbd6215 --- /dev/null +++ b/parts/.template/v1/kicad/voltage_divider/voltage_divider.sch-bak @@ -0,0 +1,55 @@ +EESchema Schematic File Version 4 +EELAYER 30 0 +EELAYER END +$Descr A4 11693 8268 +encoding utf-8 +Sheet 1 1 +Title "" +Date "" +Rev "" +Comp "" +Comment1 "" +Comment2 "" +Comment3 "" +Comment4 "" +$EndDescr +$Comp +L Device:R R1 +U 1 1 5EB5A4B7 +P 5500 3050 +F 0 "R1" H 5430 3004 50 0000 R CNN +F 1 "1k" H 5430 3095 50 0000 R CNN +F 2 "" V 5430 3050 50 0001 C CNN +F 3 "~" H 5500 3050 50 0001 C CNN + 1 5500 3050 + -1 0 0 1 +$EndComp +$Comp +L Device:R R2 +U 1 1 5EB5A7C0 +P 5500 3550 +F 0 "R2" H 5430 3504 50 0000 R CNN +F 1 "1k" H 5430 3595 50 0000 R CNN +F 2 "" V 5430 3550 50 0001 C CNN +F 3 "~" H 5500 3550 50 0001 C CNN + 1 5500 3550 + -1 0 0 1 +$EndComp +Wire Wire Line + 5500 2700 5500 2900 +Wire Wire Line + 5500 3200 5500 3300 +Wire Wire Line + 5500 3700 5500 3900 +Wire Wire Line + 5500 3300 5800 3300 +Connection ~ 5500 3300 +Wire Wire Line + 5500 3300 5500 3400 +Text HLabel 5500 2700 1 50 UnSpc ~ 0 +Vcc +Text HLabel 5500 3900 3 50 UnSpc ~ 0 +Gnd +Text HLabel 5800 3300 2 50 UnSpc ~ 0 +out +$EndSCHEMATC diff --git a/parts/.template/v1/mechanics/README.md b/parts/.template/v1/mechanics/README.md new file mode 100644 index 0000000..9cb5eec --- /dev/null +++ b/parts/.template/v1/mechanics/README.md @@ -0,0 +1,12 @@ + +# Part template mechanics (version v1) + + +These are the mechanics files of the template. +1. Edit this README + - Change title & version! + +--- +## Files + +- none \ No newline at end of file diff --git a/parts/.template/v1/requirements/README.md b/parts/.template/v1/requirements/README.md new file mode 100644 index 0000000..e1d90b8 --- /dev/null +++ b/parts/.template/v1/requirements/README.md @@ -0,0 +1,17 @@ + +# Part template requirements (version v1) + + +These are the requirements files of the part template. +1. Edit this README + - Change title & version! + +--- +## Requirements + +- none + +--- +## Constraints + +- none \ No newline at end of file diff --git a/parts/.template/v1/scripts/README.md b/parts/.template/v1/scripts/README.md new file mode 100644 index 0000000..e69de29 diff --git a/parts/Makefile b/parts/Makefile new file mode 100644 index 0000000..7f48ff4 --- /dev/null +++ b/parts/Makefile @@ -0,0 +1,2 @@ +DEPENDS = $(dir $(wildcard */Makefile)) +include $(shell git rev-parse --show-toplevel)/.make/common.mk \ No newline at end of file diff --git a/parts/README.md b/parts/README.md new file mode 100644 index 0000000..632954d --- /dev/null +++ b/parts/README.md @@ -0,0 +1,115 @@ +# LifeSensor parts +*Parts* are components which are used as building blocks of [*products*](../products/). + +--- +## List of parts +- [interconnect](./interconnect/) + - interconnect between pcbs +- [esp32-firmware](./esp32-firmware/) + - firmware for the Esp32 +- [esp32-components](./esp32-components/) + - components of the esp32 firmware + +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build all versions of all parts +- `make build` + - build all versions of all parts +- `make test` + - test all versions of all parts +- `make clean` + - clean all versions of all parts +- `make distclean` + - distclean all versions of all parts + +--- +## Adding new parts +- If you want to add a new part, +please copy the [*template part*](./.template/) and modify your copy. + +--- +## Structure + +``` +. +├── README.md : mandatory README.md you are currently reading +├── Makefile : mandatory Makefile executing targets in all subdirs +│ +├── : mandatory unique name of the part +│   ├── README.md : mandatory general description of the part +│   │ and elaboration of differences between version +│   │ +│   ├── : mandatory unique part version +│   │   ├── README.md : mandatory version specific description of the part, +│   │   │   should list all features +│   │   │ +│   │ ├── Makefile : mandatory Makefile for integration of the part +│   │   │ +│   │   ├── code : optional parent directory of any source code and build system files +│   │   │   │ i.e. firmware +│   │   │   ├── README.md : description of the source code +│   │   │   └── ... +│   │   │ +│   │   ├── docs : optional parent directory for documents +│   │   │   │ i.e. documentation, datasheets +│   │   │   ├── README.md : description of documents +│   │   │   └── ... +│   │   │ +│   │   ├── kicad : optional parent directory for kicad files +│   │   │   │ i.e. project files, schematics +│   │   │   ├── README.md : description of the kicad files +│   │   │   └── ... +│   │   │ +│   │   ├── mechanics : optional parent directory for mechanic files +│   │   │   │ i.e. 3D-printer models +│   │   │   ├── README.md : description of the mechanics files +│   │   │   └── ... +│   │   │ +│   │   ├── requirements : mandatory parent directory for requirements +│   │   │   │ used by requirement management tools +│   │   │   ├── README.md : description of requirements +│   │   │   └── ... +│   │   │ +│   │   └── scripts : optional parent directory of part specific helper scripts +│   │      │ i.e. documentation generation, calculators +│   │   ├── README.md : description of scripts +│   │      └── ... +│   │ +│   ├── +│   │   └── ... +│   └── ... +│ +├── +│   └── ... +└── ... +``` + +--- +## Rules +- *Parts* of the LifeSensor project should have a dedicated directory here. +- *Parts* should not include other *parts*, instead they should refer to other *parts*. +- *Parts* may compete for the same functionality. + - Different [*products*](../products/) may choose different *parts* +- *Parts* should follow the directory scheme defined by the [*template part*](./.template/) + - *Parent directories* may contain any structure + - Optional *parent directories* can be removed +- Only non-breaking changes may be introduced as patch for a *part* +- *Part versions* denote non-backwards-compatible variants of the same *part* + - If major implementations change, consider creating a new *part* or *part version* +- *Part versions* should never denote the state of the *part* + - All *part versions* should provide their intended functionality + +--- +## F.a.q +- Why no hierarchical structure? + - *Parts* may implement multiple features therefore can't be sorted by distinct features + - *Parts* may consist of file from multiple categories (*code*, *mechanics*, ...) breaking these up makes things complicated + - *Parts* may be outsourced in the future and reintegrated vai git submodules +- Why version directories inside git? + - Git can deduplicate files therefore the internal structure isn't bloated + - *Part versions* are not necessarily incremental changes + - This repo aims to support multiple [*products*](../products/) at once which may require different versions of *parts* \ No newline at end of file diff --git a/parts/esp32-components/Makefile b/parts/esp32-components/Makefile new file mode 100644 index 0000000..7f48ff4 --- /dev/null +++ b/parts/esp32-components/Makefile @@ -0,0 +1,2 @@ +DEPENDS = $(dir $(wildcard */Makefile)) +include $(shell git rev-parse --show-toplevel)/.make/common.mk \ No newline at end of file diff --git a/parts/esp32-components/README.md b/parts/esp32-components/README.md index 816b337..a9e29a1 100644 --- a/parts/esp32-components/README.md +++ b/parts/esp32-components/README.md @@ -1,16 +1,47 @@ -# Components + +# Part Esp32 Components + "Component" is a term for structures used by the espressif idf framework, separating code into libraries and defining dependencies. -## Adding a new component -See the [template](template/README.md) component. + -## List of Components +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build & test all variants +- `make build` + - build all variants +- `make test` + - test all variants +- `make clean` + - clean all variants +- `make distclean` + - distclean all variants -* [ulp adc](spo2/README.md) +--- +## Variants + + + +* [ulp_adc](./ulp_adc/) * Utilize the Ultra-low-power Co-processor to sample the ADCs of the ESP -* [SpO2](spo2/README.md) +* [SpO2](./spo2/) * Driver for the original remo2hbo SpO2 hardware +* [linux](./linux/) + * Source code that originates from the linux kernel +* [lifesensor_common](./lifesensor_common/) + * Commonly used structures and defines +* [channel](./channel/) + * Event abstraction for FreeRTOS +--- +## Adding a new component +See the [.template/](./.template/) component. diff --git a/parts/esp32-firmware/Makefile b/parts/esp32-firmware/Makefile new file mode 100644 index 0000000..7f48ff4 --- /dev/null +++ b/parts/esp32-firmware/Makefile @@ -0,0 +1,2 @@ +DEPENDS = $(dir $(wildcard */Makefile)) +include $(shell git rev-parse --show-toplevel)/.make/common.mk \ No newline at end of file diff --git a/parts/esp32-firmware/README.md b/parts/esp32-firmware/README.md new file mode 100644 index 0000000..9af56e5 --- /dev/null +++ b/parts/esp32-firmware/README.md @@ -0,0 +1,39 @@ + +# Part Esp32 Firmware + + +This is the firmware for the esp32 that controls the various sensor boards. + +--- +## Features + +- modular by design +- consists of components + +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build & test all variants +- `make build` + - build all variants +- `make test` + - test all variants +- `make clean` + - clean all variants +- `make distclean` + - distclean all variants + +--- +## Variants + + + +- [prototype](./prototype/) + - firmware of the current prototype +- [test](./test/) + - firmware for on-device component testing +- [qemu](./qemu/) + - firmware for component testing in qemu \ No newline at end of file diff --git a/parts/esp32-firmware/prototype/CMakeLists.txt b/parts/esp32-firmware/prototype/CMakeLists.txt index 9fefc48..9418eba 100644 --- a/parts/esp32-firmware/prototype/CMakeLists.txt +++ b/parts/esp32-firmware/prototype/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -set(EXTRA_COMPONENT_DIRS "../components" ) +set(EXTRA_COMPONENT_DIRS "components" ) get_filename_component(PROJECT_PATH "." ABSOLUTE) get_filename_component(PROJECT_NAME ${PROJECT_PATH} NAME) diff --git a/parts/esp32-firmware/prototype/Makefile b/parts/esp32-firmware/prototype/Makefile deleted file mode 120000 index 068ea8b..0000000 --- a/parts/esp32-firmware/prototype/Makefile +++ /dev/null @@ -1 +0,0 @@ -../.Makefile.mk \ No newline at end of file diff --git a/parts/esp32-firmware/prototype/Makefile b/parts/esp32-firmware/prototype/Makefile new file mode 100644 index 0000000..b91ac51 --- /dev/null +++ b/parts/esp32-firmware/prototype/Makefile @@ -0,0 +1,4 @@ +HELP = LifeSensor esp32 prototype firmware +DEPENDS = $(dir $(wildcard components/*/Makefile */Makefile) ) + +include $(shell git rev-parse --show-toplevel)/.make/idf.mk diff --git a/parts/esp32-firmware/prototype/components/channel b/parts/esp32-firmware/prototype/components/channel new file mode 120000 index 0000000..accdca2 --- /dev/null +++ b/parts/esp32-firmware/prototype/components/channel @@ -0,0 +1 @@ +../../../esp32-components/channel \ No newline at end of file diff --git a/parts/esp32-firmware/prototype/components/lifesensor_common b/parts/esp32-firmware/prototype/components/lifesensor_common new file mode 120000 index 0000000..f2784ee --- /dev/null +++ b/parts/esp32-firmware/prototype/components/lifesensor_common @@ -0,0 +1 @@ +../../../esp32-components/lifesensor_common \ No newline at end of file diff --git a/parts/esp32-firmware/prototype/components/linux b/parts/esp32-firmware/prototype/components/linux new file mode 120000 index 0000000..f65f287 --- /dev/null +++ b/parts/esp32-firmware/prototype/components/linux @@ -0,0 +1 @@ +../../../esp32-components/linux \ No newline at end of file diff --git a/parts/esp32-firmware/prototype/components/spo2 b/parts/esp32-firmware/prototype/components/spo2 new file mode 120000 index 0000000..69c3e87 --- /dev/null +++ b/parts/esp32-firmware/prototype/components/spo2 @@ -0,0 +1 @@ +../../../esp32-components/spo2 \ No newline at end of file diff --git a/parts/esp32-firmware/prototype/components/ulp_adc b/parts/esp32-firmware/prototype/components/ulp_adc new file mode 120000 index 0000000..aabc842 --- /dev/null +++ b/parts/esp32-firmware/prototype/components/ulp_adc @@ -0,0 +1 @@ +../../../esp32-components/ulp_adc \ No newline at end of file diff --git a/parts/esp32-firmware/prototype/docs/Makefile b/parts/esp32-firmware/prototype/docs/Makefile new file mode 100644 index 0000000..3ab63d4 --- /dev/null +++ b/parts/esp32-firmware/prototype/docs/Makefile @@ -0,0 +1,21 @@ +HELP = LifeSensor Documents + +TARGETS_all += build + +GRAPHVIZ += esp32_overview.svg + +.PHONY: build-graphviz +TARGETS_build += build-graphviz +HELP_build-graphviz = run graphviz to generate images +build-graphviz: $(GRAPHVIZ) + +.PHONY: clean-graphviz +TARGETS_clean += clean-graphviz +HELP_clean-graphviz = remove images produced by graphviz +clean-graphviz: + rm -f $(GRAPHVIZ) + +%.svg : %.neato.gv + neato -Tsvg $^ > $@ + +include $(shell git rev-parse --show-toplevel)/.make/common.mk \ No newline at end of file diff --git a/docs/esp32_overview.gv b/parts/esp32-firmware/prototype/docs/esp32_overview.neato.gv similarity index 100% rename from docs/esp32_overview.gv rename to parts/esp32-firmware/prototype/docs/esp32_overview.neato.gv diff --git a/docs/esp32_overview.svg b/parts/esp32-firmware/prototype/docs/esp32_overview.svg similarity index 99% rename from docs/esp32_overview.svg rename to parts/esp32-firmware/prototype/docs/esp32_overview.svg index f8adf93..7945df6 100644 --- a/docs/esp32_overview.svg +++ b/parts/esp32-firmware/prototype/docs/esp32_overview.svg @@ -1,7 +1,7 @@ - &1 | tee build/qemu.log + +build/qemu.img: build-firmware + +.PHONY: build-firmware +TARGETS_build += build-firmware +HELP_build-firmware = build image runnable by qemu +DOCKER += build-firmware +DOCKER_build-firmware = idf/v4.0 +build-firmware: + $(IDF) all + dd if=/dev/zero bs=1024 count=4096 of=build/qemu.img; \ + dd if=build/bootloader/bootloader.bin bs=1 seek=$$((0x1000)) of=build/qemu.img conv=notrunc; \ + dd if=build/partition_table/partition-table.bin bs=1 seek=$$((0x8000)) of=build/qemu.img conv=notrunc; \ + dd if=$$(ls build/*.bin) bs=1 seek=$$((0x10000)) of=build/qemu.img conv=notrunc; + +.PHONY: qemu-gdb +TARGETS += qemu-gdb +HELP_qemu-gdb = start qemu and wait for gdb +DOCKER += qemu-gdb +qemu-gdb: build/qemu.img + qemu-system-xtensa \ + -s -S \ + -no-reboot \ + -nographic \ + -machine esp32 \ + -drive file=$^,if=mtd,format=raw" + +.PHONY: gdb +TARGETS += gdb +HELP_gdb = start gdb and connect to qemu +DOCKER += gdb +gdb: + xtensa-esp32-elf-gdb $$(ls build/*.elf) \ + -ex \"target remote :1234\" \ + -ex \"tb app_main\" \ + -ex \"c\"" + +build/qemu.log: qemu + +.PHONY: test-qemu +TARGETS_test += test-qemu +HELP_test-qemu = check errors in qemu run +test-qemu: build/qemu.log + grep -q "0 Failures" $^ + + +.PHONY: clean-build +TARGETS_clean += clean-build +HELP_clean-build = remove generated files of project components +clean-build: + rm -rf build/esp-idf/ + +.PHONY: distclean-build +TARGETS_distclean += distclean-build +HELP_distclean-build = remove all generated files +distclean-build: + rm -f sdkconfig + rm -rf build + +include $(shell git rev-parse --show-toplevel)/.make/common.mk \ No newline at end of file diff --git a/parts/esp32-firmware/qemu/components/channel b/parts/esp32-firmware/qemu/components/channel new file mode 120000 index 0000000..accdca2 --- /dev/null +++ b/parts/esp32-firmware/qemu/components/channel @@ -0,0 +1 @@ +../../../esp32-components/channel \ No newline at end of file diff --git a/parts/esp32-firmware/qemu/components/lifesensor_common b/parts/esp32-firmware/qemu/components/lifesensor_common new file mode 120000 index 0000000..f2784ee --- /dev/null +++ b/parts/esp32-firmware/qemu/components/lifesensor_common @@ -0,0 +1 @@ +../../../esp32-components/lifesensor_common \ No newline at end of file diff --git a/parts/esp32-firmware/qemu/components/linux b/parts/esp32-firmware/qemu/components/linux new file mode 120000 index 0000000..f65f287 --- /dev/null +++ b/parts/esp32-firmware/qemu/components/linux @@ -0,0 +1 @@ +../../../esp32-components/linux \ No newline at end of file diff --git a/parts/esp32-firmware/qemu/components/spo2 b/parts/esp32-firmware/qemu/components/spo2 new file mode 120000 index 0000000..69c3e87 --- /dev/null +++ b/parts/esp32-firmware/qemu/components/spo2 @@ -0,0 +1 @@ +../../../esp32-components/spo2 \ No newline at end of file diff --git a/parts/esp32-firmware/qemu/components/ulp_adc b/parts/esp32-firmware/qemu/components/ulp_adc new file mode 120000 index 0000000..aabc842 --- /dev/null +++ b/parts/esp32-firmware/qemu/components/ulp_adc @@ -0,0 +1 @@ +../../../esp32-components/ulp_adc \ No newline at end of file diff --git a/parts/esp32-firmware/test/.gitignore b/parts/esp32-firmware/test/.gitignore index f2e74fa..14bde84 100644 --- a/parts/esp32-firmware/test/.gitignore +++ b/parts/esp32-firmware/test/.gitignore @@ -1,3 +1,4 @@ build sdkconfig* !sdkconfig.defaults +.setup-device.mk \ No newline at end of file diff --git a/parts/esp32-firmware/test/CMakeLists.txt b/parts/esp32-firmware/test/CMakeLists.txt index c16ed85..1102b5e 100644 --- a/parts/esp32-firmware/test/CMakeLists.txt +++ b/parts/esp32-firmware/test/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -set(EXTRA_COMPONENT_DIRS "../components" ) +set(EXTRA_COMPONENT_DIRS "components/" ) set(PROJECT_COMPONENTS "") @@ -15,12 +15,7 @@ endforeach() get_filename_component(PROJECT_PATH "." ABSOLUTE) get_filename_component(PROJECT_NAME ${PROJECT_PATH} NAME) -set( - TEST_COMPONENTS - ${PROJECT_COMPONENTS} - CACHE STRING - "List of components to test" -) +set(TEST_COMPONENTS ${PROJECT_COMPONENTS}) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(${PROJECT_NAME}) diff --git a/parts/esp32-firmware/test/Makefile b/parts/esp32-firmware/test/Makefile deleted file mode 120000 index 068ea8b..0000000 --- a/parts/esp32-firmware/test/Makefile +++ /dev/null @@ -1 +0,0 @@ -../.Makefile.mk \ No newline at end of file diff --git a/parts/esp32-firmware/test/Makefile b/parts/esp32-firmware/test/Makefile new file mode 100644 index 0000000..1abed14 --- /dev/null +++ b/parts/esp32-firmware/test/Makefile @@ -0,0 +1,4 @@ +HELP = LifeSensor esp32 test firmware +DEPENDS = $(dir $(wildcard components/*/Makefile)) + +include $(shell git rev-parse --show-toplevel)/.make/idf.mk \ No newline at end of file diff --git a/parts/esp32-firmware/test/components/channel b/parts/esp32-firmware/test/components/channel new file mode 120000 index 0000000..accdca2 --- /dev/null +++ b/parts/esp32-firmware/test/components/channel @@ -0,0 +1 @@ +../../../esp32-components/channel \ No newline at end of file diff --git a/parts/esp32-firmware/test/components/lifesensor_common b/parts/esp32-firmware/test/components/lifesensor_common new file mode 120000 index 0000000..f2784ee --- /dev/null +++ b/parts/esp32-firmware/test/components/lifesensor_common @@ -0,0 +1 @@ +../../../esp32-components/lifesensor_common \ No newline at end of file diff --git a/parts/esp32-firmware/test/components/linux b/parts/esp32-firmware/test/components/linux new file mode 120000 index 0000000..f65f287 --- /dev/null +++ b/parts/esp32-firmware/test/components/linux @@ -0,0 +1 @@ +../../../esp32-components/linux \ No newline at end of file diff --git a/parts/esp32-firmware/test/components/spo2 b/parts/esp32-firmware/test/components/spo2 new file mode 120000 index 0000000..69c3e87 --- /dev/null +++ b/parts/esp32-firmware/test/components/spo2 @@ -0,0 +1 @@ +../../../esp32-components/spo2 \ No newline at end of file diff --git a/parts/esp32-firmware/test/components/ulp_adc b/parts/esp32-firmware/test/components/ulp_adc new file mode 120000 index 0000000..aabc842 --- /dev/null +++ b/parts/esp32-firmware/test/components/ulp_adc @@ -0,0 +1 @@ +../../../esp32-components/ulp_adc \ No newline at end of file diff --git a/parts/interconnect/Makefile b/parts/interconnect/Makefile new file mode 100644 index 0000000..7f48ff4 --- /dev/null +++ b/parts/interconnect/Makefile @@ -0,0 +1,2 @@ +DEPENDS = $(dir $(wildcard */Makefile)) +include $(shell git rev-parse --show-toplevel)/.make/common.mk \ No newline at end of file diff --git a/parts/interconnect/README.md b/parts/interconnect/README.md new file mode 100644 index 0000000..8ab938d --- /dev/null +++ b/parts/interconnect/README.md @@ -0,0 +1,37 @@ + +# Part interconnect + + +This is the interconnect that connects the various sensor boards. + +--- +## Features + +- single component +- stackable +- raspberry pi compatible pinout + - raspberry pi can be used as test platform to drive the interconnect + +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build & test all versions +- `make build` + - build all versions +- `make test` + - test all versions +- `make clean` + - clean all versions +- `make distclean` + - distclean all version + +--- +## Versions + + + +- [v1](./v1/) + - initial version \ No newline at end of file diff --git a/parts/interconnect/v1/Makefile b/parts/interconnect/v1/Makefile new file mode 100644 index 0000000..d87a11c --- /dev/null +++ b/parts/interconnect/v1/Makefile @@ -0,0 +1,2 @@ +DEPENDS = $(dir $(wildcard */Makefile)) +include $(shell git rev-parse --show-toplevel)/.make/part.mk \ No newline at end of file diff --git a/parts/interconnect/v1/README.md b/parts/interconnect/v1/README.md new file mode 100644 index 0000000..5dd7e98 --- /dev/null +++ b/parts/interconnect/v1/README.md @@ -0,0 +1,44 @@ + +# Part interconnect (version v1) + + +This is the interconnect that connects the various sensor boards. + +--- +## Features + +- single component +- stackable +- raspberry pi compatible pinout + - raspberry pi can be used as test platform to drive the interconnect + +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build & test everything +- `make build` + - build everything +- `make test` + - test everything +- `make clean` + - clean everything +- `make distclean` + - distclean everything + +---- +## [Docs](./docs/) + + +- list of vendors + +--- +## [Kicad](./kicad/) + + +- pinout schematic + +--- +See the other READMEs or [Makefile](./Makefile) for more information \ No newline at end of file diff --git a/parts/interconnect/v1/docs/README.md b/parts/interconnect/v1/docs/README.md new file mode 100644 index 0000000..d592c29 --- /dev/null +++ b/parts/interconnect/v1/docs/README.md @@ -0,0 +1,11 @@ + +# Part interconnect documents (version v1) + + +This the document collection of the part interconnect. + +--- +## Documents + +- [vendors.md](./vendors.md) + - List of shops selling the interconnect \ No newline at end of file diff --git a/parts/interconnect/v1/docs/vendors.md b/parts/interconnect/v1/docs/vendors.md new file mode 100644 index 0000000..1e84c6a --- /dev/null +++ b/parts/interconnect/v1/docs/vendors.md @@ -0,0 +1,13 @@ +# Vendor list + +## aliexpress.com +- [Company Connector & Switch Store](https://de.aliexpress.com/item/32829482875.html) + - 2.50$/5pc + +## amazon.com +- [SCHMARTBOARD](https://www.amazon.com/Extra-Long-Female-Stackable-Headers/dp/B06XR5ZPD9) + - 5$/3pc + +## digikey.de +- [SCHMARTBOARD](https://www.digikey.de/products/de?keywords=%20920-0200-01%20) + - 4.61€/3pc \ No newline at end of file diff --git a/parts/interconnect/v1/kicad/README.md b/parts/interconnect/v1/kicad/README.md new file mode 100644 index 0000000..c84c7e5 --- /dev/null +++ b/parts/interconnect/v1/kicad/README.md @@ -0,0 +1,11 @@ + +# Part interconnect kicad (version v1) + + +The kicad schematics of the interconnect. + +--- +## Projects + +- [interconnect](./interconnect/) + - the interconnect schematics \ No newline at end of file diff --git a/parts/interconnect/v1/kicad/interconnect/interconnect.kicad_pcb b/parts/interconnect/v1/kicad/interconnect/interconnect.kicad_pcb new file mode 100644 index 0000000..02c8ecb --- /dev/null +++ b/parts/interconnect/v1/kicad/interconnect/interconnect.kicad_pcb @@ -0,0 +1 @@ +(kicad_pcb (version 4) (host kicad "dummy file") ) diff --git a/parts/interconnect/v1/kicad/interconnect/interconnect.pro b/parts/interconnect/v1/kicad/interconnect/interconnect.pro new file mode 100644 index 0000000..152769c --- /dev/null +++ b/parts/interconnect/v1/kicad/interconnect/interconnect.pro @@ -0,0 +1,33 @@ +update=22/05/2015 07:44:53 +version=1 +last_client=kicad +[general] +version=1 +RootSch= +BoardNm= +[pcbnew] +version=1 +LastNetListRead= +UseCmpFile=1 +PadDrill=0.600000000000 +PadDrillOvalY=0.600000000000 +PadSizeH=1.500000000000 +PadSizeV=1.500000000000 +PcbTextSizeV=1.500000000000 +PcbTextSizeH=1.500000000000 +PcbTextThickness=0.300000000000 +ModuleTextSizeV=1.000000000000 +ModuleTextSizeH=1.000000000000 +ModuleTextSizeThickness=0.150000000000 +SolderMaskClearance=0.000000000000 +SolderMaskMinWidth=0.000000000000 +DrawSegmentWidth=0.200000000000 +BoardOutlineThickness=0.100000000000 +ModuleOutlineThickness=0.150000000000 +[cvpcb] +version=1 +NetIExt=net +[eeschema] +version=1 +LibDir= +[eeschema/libraries] diff --git a/parts/interconnect/v1/kicad/interconnect/interconnect.sch b/parts/interconnect/v1/kicad/interconnect/interconnect.sch new file mode 100644 index 0000000..cfb1962 --- /dev/null +++ b/parts/interconnect/v1/kicad/interconnect/interconnect.sch @@ -0,0 +1,451 @@ +EESchema Schematic File Version 4 +EELAYER 30 0 +EELAYER END +$Descr A4 11693 8268 +encoding utf-8 +Sheet 2 2 +Title "" +Date "" +Rev "" +Comp "" +Comment1 "" +Comment2 "" +Comment3 "" +Comment4 "" +$EndDescr +$Comp +L Connector_Generic:Conn_02x20_Odd_Even J1 +U 1 1 5EB2A561 +P 5800 3800 +F 0 "J1" H 5850 4917 50 0000 C CNN +F 1 "Conn_02x20_Odd_Even" H 5850 4826 50 0000 C CNN +F 2 "Connector_IDC:IDC-Header_2x20_P2.54mm_Vertical" H 5800 3800 50 0001 C CNN +F 3 "~" H 5800 3800 50 0001 C CNN + 1 5800 3800 + 1 0 0 -1 +$EndComp +Text Label 4900 3000 0 50 ~ 0 +BCM2 +Text Label 5250 2900 0 50 ~ 0 +3v3 +Text Label 4900 3100 0 50 ~ 0 +BCM3 +Text Label 4900 3200 0 50 ~ 0 +BCM4 +Text Label 4900 3400 0 50 ~ 0 +BCM17 +Text Label 4900 3500 0 50 ~ 0 +BCM27 +Text Label 4900 3600 0 50 ~ 0 +BCM22 +Text Label 5250 3700 0 50 ~ 0 +3v3 +Text Label 4900 3800 0 50 ~ 0 +BCM10 +Text Label 4900 3900 0 50 ~ 0 +BCM9 +Text Label 4900 4000 0 50 ~ 0 +BCM11 +Text Label 4900 4200 0 50 ~ 0 +BCM0 +Text Label 4900 4300 0 50 ~ 0 +BCM5 +Text Label 4900 4400 0 50 ~ 0 +BCM6 +Text Label 4900 4500 0 50 ~ 0 +BCM13 +Text Label 4900 4600 0 50 ~ 0 +BCM19 +Text Label 4900 4700 0 50 ~ 0 +BCM26 +Text Label 6450 2900 0 50 ~ 0 +5v +Text Label 6450 3000 0 50 ~ 0 +5v +Wire Notes Line + 5250 2600 5250 4850 +Text Label 5250 3300 0 50 ~ 0 +GND +Text Label 5250 4100 0 50 ~ 0 +GND +Text Label 5250 4800 0 50 ~ 0 +GND +Wire Notes Line + 6450 2600 6450 4850 +Text Label 6800 4800 0 50 ~ 0 +BCM21 +Text Label 6800 4700 0 50 ~ 0 +BCM20 +Text Label 6800 4600 0 50 ~ 0 +BCM16 +Text Label 6800 4400 0 50 ~ 0 +BCM12 +Text Label 6800 4200 0 50 ~ 0 +BCM1 +Text Label 6800 4100 0 50 ~ 0 +BCM7 +Text Label 6800 4000 0 50 ~ 0 +BCM8 +Text Label 6800 3900 0 50 ~ 0 +BCM25 +Text Label 6800 3700 0 50 ~ 0 +BCM24 +Text Label 6800 3600 0 50 ~ 0 +BCM23 +Text Label 6800 3400 0 50 ~ 0 +BCM18 +Text Label 6800 3300 0 50 ~ 0 +BCM15 +Text Label 6800 3200 0 50 ~ 0 +BCM14 +Text Label 6450 3100 0 50 ~ 0 +GND +Text Label 6450 3500 0 50 ~ 0 +GND +Text Label 6450 3800 0 50 ~ 0 +GND +Text Label 6450 4300 0 50 ~ 0 +GND +Text Label 6450 4500 0 50 ~ 0 +GND +Wire Notes Line + 4900 2600 4900 4850 +Wire Notes Line + 6800 2600 6800 4850 +Text Notes 4900 2600 0 50 ~ 0 +RPI\nBCM +Text Notes 5250 2600 0 50 ~ 0 +RPI\nPower +Text Notes 6450 2600 0 50 ~ 0 +RPI\nPower +Text Notes 6800 2600 0 50 ~ 0 +RPI\nBCM +Text Label 4550 3000 0 50 ~ 0 +W8 +Text Label 4550 3100 0 50 ~ 0 +W9 +Text Label 4550 3200 0 50 ~ 0 +W7 +Text Label 4550 3400 0 50 ~ 0 +W0 +Text Label 4550 3500 0 50 ~ 0 +W2 +Text Label 4550 3600 0 50 ~ 0 +W3 +Text Label 4550 3800 0 50 ~ 0 +W12 +Text Label 4550 3900 0 50 ~ 0 +W13 +Text Label 4550 4000 0 50 ~ 0 +W14 +Text Label 4550 4200 0 50 ~ 0 +W30 +Text Label 4550 4300 0 50 ~ 0 +W21 +Text Label 4550 4400 0 50 ~ 0 +W22 +Text Label 4550 4500 0 50 ~ 0 +W23 +Text Label 4550 4600 0 50 ~ 0 +W24 +Text Label 4550 4700 0 50 ~ 0 +W25 +Wire Notes Line + 4550 2600 4550 4850 +Text Notes 4550 2600 0 50 ~ 0 +RPI\nWiring +Text Label 7150 3200 0 50 ~ 0 +W15 +Text Label 7150 3300 0 50 ~ 0 +W16 +Text Label 7150 3400 0 50 ~ 0 +W1 +Text Label 7150 3600 0 50 ~ 0 +W4 +Text Label 7150 3700 0 50 ~ 0 +W5 +Text Label 7150 3900 0 50 ~ 0 +W6 +Text Label 7150 4000 0 50 ~ 0 +W10 +Text Label 7150 4100 0 50 ~ 0 +W11 +Text Label 7150 4200 0 50 ~ 0 +W31 +Text Label 7150 4400 0 50 ~ 0 +W26 +Text Label 7150 4600 0 50 ~ 0 +W27 +Text Label 7150 4700 0 50 ~ 0 +W28 +Text Label 7150 4800 0 50 ~ 0 +W29 +Wire Notes Line + 7150 2600 7150 4850 +Text Notes 7150 2600 0 50 ~ 0 +RPI\nWiring +Text Label 4100 3000 0 50 ~ 0 +I2C1_SDA +Text Label 4100 3100 0 50 ~ 0 +I2C1_SCL +Text Label 4100 3400 0 50 ~ 0 +SP1_CE1 +Text Label 4100 3800 0 50 ~ 0 +SPI0_MOSI +Text Label 4100 3900 0 50 ~ 0 +SPI0_MISO +Text Label 4100 4000 0 50 ~ 0 +SPI0_SCLK +Text Label 4100 4600 0 50 ~ 0 +SPI1_MISO +Wire Notes Line + 4100 2600 4100 4850 +Text Notes 4100 2600 0 50 ~ 0 +RPI\nBus +Text Label 7600 3400 0 50 ~ 0 +SPI1_CE0 +Text Label 7600 4000 0 50 ~ 0 +SPI0_CE0 +Text Label 7600 4100 0 50 ~ 0 +SPI0_CE1 +Text Label 7600 4600 0 50 ~ 0 +SPI1_CE2 +Text Label 7600 4700 0 50 ~ 0 +SPI1_MOSI +Text Label 7600 4800 0 50 ~ 0 +SPI1_SCLK +Wire Notes Line + 7600 2600 7600 4850 +Text Notes 7600 2600 0 50 ~ 0 +RPI\nBus +Text HLabel 3600 2900 0 50 UnSpc ~ 0 +SENS_3V3_ANA +Text HLabel 3600 3000 0 50 UnSpc ~ 0 +SDA +Text HLabel 3600 3100 0 50 UnSpc ~ 0 +SCL +Text HLabel 3600 3200 0 50 UnSpc ~ 0 +SENS_BPM_DRDY +Text HLabel 3600 3400 0 50 UnSpc ~ 0 +SENS_ECG_DRDY +Text HLabel 3600 3500 0 50 UnSpc ~ 0 +SENS_START +Text HLabel 3600 3600 0 50 UnSpc ~ 0 +SENS_RST_BPM_n +Text HLabel 3600 3800 0 50 UnSpc ~ 0 +SENS_MOSI +Text HLabel 3600 3900 0 50 UnSpc ~ 0 +SENS_MISO +Text HLabel 3600 4000 0 50 UnSpc ~ 0 +SENS_SCLK +Text HLabel 8100 2900 2 50 UnSpc ~ 0 +SENS_5V_ANA +Text HLabel 8100 3000 2 50 UnSpc ~ 0 +SENS_5V_ANA +Text HLabel 8100 3200 2 50 UnSpc ~ 0 +SENS_IO0 +Text HLabel 8100 3300 2 50 UnSpc ~ 0 +SENS_IO1 +Text HLabel 8100 3400 2 50 UnSpc ~ 0 +SENS_SPO2_DRDY +Text HLabel 8100 3600 2 50 UnSpc ~ 0 +SENS_RST_ECG_n +Text HLabel 8100 3700 2 50 UnSpc ~ 0 +SENS_RST_SPO2_n +Text HLabel 8100 3900 2 50 UnSpc ~ 0 +SENS_CS_BPM_n +Text HLabel 8100 4000 2 50 UnSpc ~ 0 +SENS_CS_ECG_n +Text HLabel 8100 4100 2 50 UnSpc ~ 0 +SENS_CS_SPO2_n +Wire Notes Line + 8100 2600 8100 4850 +Text Notes 8100 2600 0 50 ~ 0 +LifeSensor +Wire Notes Line + 3600 2600 3600 4850 +Text Notes 3600 2600 0 50 ~ 0 +LifeSensor +Wire Wire Line + 3600 2900 5600 2900 +Wire Wire Line + 6100 2900 8100 2900 +Wire Wire Line + 3600 3000 5600 3000 +Wire Wire Line + 6100 3000 8100 3000 +$Comp +L power:GND #PWR0101 +U 1 1 5EB524D0 +P 8100 3100 +F 0 "#PWR0101" H 8100 2850 50 0001 C CNN +F 1 "GND" V 8105 2972 50 0000 R CNN +F 2 "" H 8100 3100 50 0001 C CNN +F 3 "" H 8100 3100 50 0001 C CNN + 1 8100 3100 + 0 -1 -1 0 +$EndComp +$Comp +L power:GND #PWR0102 +U 1 1 5EB5282B +P 8100 3500 +F 0 "#PWR0102" H 8100 3250 50 0001 C CNN +F 1 "GND" V 8105 3372 50 0000 R CNN +F 2 "" H 8100 3500 50 0001 C CNN +F 3 "" H 8100 3500 50 0001 C CNN + 1 8100 3500 + 0 -1 -1 0 +$EndComp +$Comp +L power:GND #PWR0103 +U 1 1 5EB52A8A +P 8100 3800 +F 0 "#PWR0103" H 8100 3550 50 0001 C CNN +F 1 "GND" V 8105 3672 50 0000 R CNN +F 2 "" H 8100 3800 50 0001 C CNN +F 3 "" H 8100 3800 50 0001 C CNN + 1 8100 3800 + 0 -1 -1 0 +$EndComp +$Comp +L power:GND #PWR0104 +U 1 1 5EB52C69 +P 8100 4300 +F 0 "#PWR0104" H 8100 4050 50 0001 C CNN +F 1 "GND" V 8105 4172 50 0000 R CNN +F 2 "" H 8100 4300 50 0001 C CNN +F 3 "" H 8100 4300 50 0001 C CNN + 1 8100 4300 + 0 -1 -1 0 +$EndComp +$Comp +L power:GND #PWR0105 +U 1 1 5EB52E33 +P 8100 4500 +F 0 "#PWR0105" H 8100 4250 50 0001 C CNN +F 1 "GND" V 8105 4372 50 0000 R CNN +F 2 "" H 8100 4500 50 0001 C CNN +F 3 "" H 8100 4500 50 0001 C CNN + 1 8100 4500 + 0 -1 -1 0 +$EndComp +$Comp +L power:GND #PWR0106 +U 1 1 5EB52FAB +P 3600 4800 +F 0 "#PWR0106" H 3600 4550 50 0001 C CNN +F 1 "GND" V 3605 4672 50 0000 R CNN +F 2 "" H 3600 4800 50 0001 C CNN +F 3 "" H 3600 4800 50 0001 C CNN + 1 3600 4800 + 0 1 1 0 +$EndComp +$Comp +L power:GND #PWR0107 +U 1 1 5EB53392 +P 3600 4100 +F 0 "#PWR0107" H 3600 3850 50 0001 C CNN +F 1 "GND" V 3605 3972 50 0000 R CNN +F 2 "" H 3600 4100 50 0001 C CNN +F 3 "" H 3600 4100 50 0001 C CNN + 1 3600 4100 + 0 1 1 0 +$EndComp +$Comp +L power:GND #PWR0108 +U 1 1 5EB5355E +P 3600 3300 +F 0 "#PWR0108" H 3600 3050 50 0001 C CNN +F 1 "GND" V 3605 3172 50 0000 R CNN +F 2 "" H 3600 3300 50 0001 C CNN +F 3 "" H 3600 3300 50 0001 C CNN + 1 3600 3300 + 0 1 1 0 +$EndComp +Wire Wire Line + 3600 3100 5600 3100 +Wire Wire Line + 6100 3100 8100 3100 +Wire Wire Line + 8100 3200 6100 3200 +Wire Wire Line + 5600 3200 3600 3200 +Wire Wire Line + 3600 3300 5600 3300 +Wire Wire Line + 6100 3300 8100 3300 +Wire Wire Line + 8100 3400 6100 3400 +Wire Wire Line + 5600 3400 3600 3400 +Wire Wire Line + 3600 3500 5600 3500 +Wire Wire Line + 6100 3500 8100 3500 +Wire Wire Line + 8100 3600 6100 3600 +Wire Wire Line + 5600 3600 3600 3600 +Text HLabel 3600 3700 0 50 UnSpc ~ 0 +SENS_3V3_DIG +Wire Wire Line + 3600 3700 5600 3700 +Wire Wire Line + 6100 3700 8100 3700 +Wire Wire Line + 8100 3800 6100 3800 +Wire Wire Line + 5600 3800 3600 3800 +Wire Wire Line + 3600 3900 5600 3900 +Wire Wire Line + 6100 3900 8100 3900 +Wire Wire Line + 8100 4000 6100 4000 +Wire Wire Line + 5600 4000 3600 4000 +Wire Wire Line + 3600 4100 5600 4100 +Wire Wire Line + 6100 4100 8100 4100 +Wire Wire Line + 3600 4200 5600 4200 +Wire Wire Line + 6100 4200 8100 4200 +Wire Wire Line + 8100 4300 6100 4300 +Wire Wire Line + 5600 4300 3600 4300 +Wire Wire Line + 3600 4400 5600 4400 +Wire Wire Line + 6100 4400 8100 4400 +Wire Wire Line + 8100 4500 6100 4500 +Wire Wire Line + 5600 4500 3600 4500 +Wire Wire Line + 3600 4600 5600 4600 +Wire Wire Line + 6100 4600 8100 4600 +Wire Wire Line + 8100 4700 6100 4700 +Wire Wire Line + 5600 4700 3600 4700 +Wire Wire Line + 3600 4800 5600 4800 +Wire Wire Line + 6100 4800 8100 4800 +NoConn ~ 3600 4200 +NoConn ~ 3600 4300 +NoConn ~ 3600 4400 +NoConn ~ 3600 4500 +NoConn ~ 3600 4600 +NoConn ~ 3600 4700 +NoConn ~ 8100 4200 +NoConn ~ 8100 4400 +NoConn ~ 8100 4600 +NoConn ~ 8100 4700 +NoConn ~ 8100 4800 +NoConn ~ 8950 4900 +$EndSCHEMATC diff --git a/products/.template/README.md b/products/.template/README.md new file mode 100644 index 0000000..ec57556 --- /dev/null +++ b/products/.template/README.md @@ -0,0 +1,40 @@ + +# Product template + + +This template should be used to create a new product. +1. Copy this template directory +2. Give your new product a meaningful name. +3. Append your new product to the product list in [../README.md](../README.md) +4. Edit this README + - Change title! +5. Continue editing in [v1/README.md](./v1/README.md) + +--- +## Product features + +- provides template for product + +--- +## Product versions + + + +- [v1/](./v1/) + - initial version + +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build & test all versions +- `make build` + - build all versions +- `make test` + - test all versions +- `make clean` + - clean all versions +- `make distclean` + - distclean all version \ No newline at end of file diff --git a/products/.template/v1/Makefile b/products/.template/v1/Makefile new file mode 100644 index 0000000..99bd0d1 --- /dev/null +++ b/products/.template/v1/Makefile @@ -0,0 +1,5 @@ +### PARTS OF THE PRODUCT ### +PARTS += #empty +PARTS += interconnect/v1 + +include $(shell git rev-parse --show-toplevel)/.make/product.mk \ No newline at end of file diff --git a/products/.template/v1/README.md b/products/.template/v1/README.md new file mode 100644 index 0000000..60cd9cc --- /dev/null +++ b/products/.template/v1/README.md @@ -0,0 +1,47 @@ + +# Product template (version v1) + + +This template should be used to create a new product version. +1. Edit this README + - Change title & version! +2. Delete unneeded directories +3. Edit READMEs of directories +4. Edit Makefile + +--- +## Features + +- provides template for product version + +---- +## Directories + +### [docs/](./docs/) + + +- template docs directory + +### [requirements/](./requirements/) + + +- template requirements directory + +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build & test everything +- `make build` + - build everything +- `make test` + - test everything +- `make clean` + - clean everything +- `make distclean` + - distclean everything + +--- +See the other READMEs or [Makefile](./Makefile) for more information \ No newline at end of file diff --git a/products/.template/v1/docs/README.md b/products/.template/v1/docs/README.md new file mode 100644 index 0000000..b1c49a6 --- /dev/null +++ b/products/.template/v1/docs/README.md @@ -0,0 +1,12 @@ + +# Part template documents (version v1) + + +This the document collection of the product template. +1. Edit this README + - Change title & version! + +--- +## Documents + +- none \ No newline at end of file diff --git a/products/.template/v1/requirements/README.md b/products/.template/v1/requirements/README.md new file mode 100644 index 0000000..c4e4f3f --- /dev/null +++ b/products/.template/v1/requirements/README.md @@ -0,0 +1,17 @@ + +# Product template requirements (version v1) + + +These are the requirements files of the product template. +1. Edit this README + - Change title & version! + +--- +## Requirements + +- none + +--- +## Constraints + +- none \ No newline at end of file diff --git a/products/Makefile b/products/Makefile new file mode 100644 index 0000000..7f48ff4 --- /dev/null +++ b/products/Makefile @@ -0,0 +1,2 @@ +DEPENDS = $(dir $(wildcard */Makefile)) +include $(shell git rev-parse --show-toplevel)/.make/common.mk \ No newline at end of file diff --git a/products/README.md b/products/README.md new file mode 100644 index 0000000..7cc4c72 --- /dev/null +++ b/products/README.md @@ -0,0 +1,81 @@ +# LifeSensor products +*Products* are supported [*LifeSensor*](https://lifesensor.org) devices consisting of [*parts*](../parts/). + +--- +## Products +- [prototype/](./prototype/) + - prototype & testing ground of various parts + +--- +## Products structure + +``` +lifesensor/products +│ +├── README.md : mandatory README.md you are currently reading +├── Makefile : mandatory Makefile executing targets in all subdirs +│ +├── : mandatory unique name of the product +│   ├── README.md : mandatory general description of the product +│   │ and elaboration of differences between version +│   │ +│   ├── : mandatory unique product version +│   │   ├── README.md : mandatory version specific description of the product, +│   │   │   should list all features +│   │   │ +│   │ ├── Makefile : mandatory Makefile for integration of the product +│   │   │ +│   │   ├── docs : optional parent directory for documents +│   │   │   │ i.e. documentation, datasheets +│   │   │   ├── README.md : description of documents +│   │   │   └── ... +│   │   │ +│   │   └── requirements : mandatory parent directory for requirements +│   │      │ used by requirement management tools +│   │      ├── README.md : description of requirements +│   │      └── ... +│   │ +│   ├── +│   │   └── ... +│   └── ... +│ +├── +│   └── ... +└── ... +``` + +--- +## Makefile + +- `make help` + - show Makefile options +- `make all` + - build & test all products +- `make build` + - build all products +- `make test` + - test all products +- `make clean` + - clean all products +- `make distclean` + - distclean all products + +--- +## Adding new products +- If you want to add a new product, +please copy the [*template product*](./.template/) and modify your copy. + +--- +## Rules +- *Products* of the [*LifeSensor*](https://lifesensor.org) project should have a dedicated directory here. +- *Products* should not include [*parts*](../parts/) or other *products* +- *Products* may compete for the same functionality. + - Different *products* may choose different [*parts*](../parts/) to achieve the same +- *Products* should follow the directory scheme defined by the [*template product*](./.template/) + - *Parent directories* may contain any structure + - Optional *parent directories* can be removed +- Only non-breaking changes may be introduced as patch for a *product* +- *Products versions* denote non-backwards-compatible variants of the same *product* + - If major implementations change, consider creating a new *product* or *product version* +- *Products versions* should never denote the state of the *product* + - All *product versions* should provide their intended functionality From c1990724a73dd3088ee4249adaf781a9c283823b Mon Sep 17 00:00:00 2001 From: drechsler Date: Thu, 19 Nov 2020 21:54:40 +0100 Subject: [PATCH 03/12] made help of variables target-aware --- .make/help.mk | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.make/help.mk b/.make/help.mk index cd9228e..9ef4ec4 100644 --- a/.make/help.mk +++ b/.make/help.mk @@ -76,4 +76,17 @@ help-%: $(TARGETS_$(@:help-%=%)), \ [ -n "$(HELP_$(target))" ] && echo "make $(target)" || true; \ [ -n "$(HELP_$(target))" ] && echo -e "\t$(HELP_$(target))" || true; \ + ) + @$(foreach \ + group, \ + $(VARIABLES_$(@:help-%=%)), \ + $(warning $(group)) \ + [ -n "$(VARIABLES_$(group))" ] && echo -e "\n--- $(HELP_$(group)) ---" || true; \ + $(foreach \ + variable, \ + $(VARIABLES_$(group)), \ + echo "$(variable)"; \ + [ -n "$(HELP_$(variable))" ] && echo -e "\t$(HELP_$(variable))" || true; \ + echo -e "\t(currently: $($(variable)))"; \ + ) \ ) \ No newline at end of file From 22605d27277dd3a67fd975c1dcd76d356acfd2c1 Mon Sep 17 00:00:00 2001 From: drechsler Date: Thu, 19 Nov 2020 21:55:09 +0100 Subject: [PATCH 04/12] added templating mechanic to Makefiles --- .make/common.mk | 2 + .make/templating.mk | 95 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 .make/templating.mk diff --git a/.make/common.mk b/.make/common.mk index 0518c71..057cca3 100644 --- a/.make/common.mk +++ b/.make/common.mk @@ -131,5 +131,7 @@ $(foreach \ $(eval $(call TEMPLATE_NODOCKER_TARGET,$(TARGET))) \ ) +include $(shell git rev-parse --show-toplevel)/.make/templating.mk + TARGETS := $(TARGETS_common) $(TARGETS) include $(shell git rev-parse --show-toplevel)/.make/help.mk diff --git a/.make/templating.mk b/.make/templating.mk new file mode 100644 index 0000000..fc71cf9 --- /dev/null +++ b/.make/templating.mk @@ -0,0 +1,95 @@ + +-include .template.mk + +ifdef TEMPLATING_CHOICES + +VARIABLES += TEMPLATING +HELP_TEMPLATING := Templating options + +VARIABLES_TEMPLATING += TEMPLATING_CHOICES +HELP_TEMPLATING_CHOICES := existing templates + +VARIABLES_new += TEMPLATING_COMMON +HELP_TEMPLATING_COMMON := Common template options + +VARIABLES_TEMPLATING_COMMON += NAME +HELP_NAME := how to rename the template +NAME ?= #empty + +TEMPLATING_SUFFIXES += %.m4 + +define TEMPLATE_NEW_TEMPLATING_TARGET + +$$(if $$(TEMPLATING_PATH_$1),,$$(error Variable 'TEMPLATING_PATH_$1' is not set in .template.mk)) +$$(if $$(TEMPLATING_HELP_$1),,$$(error Variable 'TEMPLATING_HELP_$1' is not set in .template.mk)) + +VARIABLES_new += new-$1-vars +VARIABLES_new-$1-vars := $$(TEMPLATING_VARS_$1) +HELP_new-$1-vars := 'new-$1' template options + + +TEMPLATING_INPUT_$1 := $$(foreach PATH,$$(TEMPLATING_PATH_$1),$$(shell find $$(PATH) -type f)) +TEMPLATING_FILES_$1 := $$(foreach PATH,$$(TEMPLATING_PATH_$1), $$(shell find $$(PATH) -type f -printf '%P\n') ) +TEMPLATING_INDEX_$1 := $$(shell for i in {1..$$(words $$(TEMPLATING_FILES_$1))}; do echo $$$$i; done) +TEMPLATING_M4_$1 := m4 $$(foreach DEFINE,$$(TEMPLATING_VARS_$1),-D__$$(DEFINE)__=$$($$(DEFINE))) +TEMPLATING_COPY_$1 := $$(shell echo '$$(addprefix $$(NAME)/, $$(TEMPLATING_FILES_$1))' | $$(TEMPLATING_M4_$1)) +TEMPLATING_OUTPUT_$1 := $$(patsubst $$(TEMPLATING_SUFFIXES),%,$$(TEMPLATING_COPY_$1)) + +.PHONY: _check-new-$1 +_check-new-$1: + $$(foreach \ + VAR, \ + $$(VARIABLES_TEMPLATING_COMMON) $$(TEMPLATING_VARS_$1), \ + $$(if \ + $$($$(VAR)), \ + , \ + $$(error option $$(VAR) is missing. please specify via '$$(VAR)='. see 'make help-new' for more information) \ + ) \ + ) + +.PHONY: new-$1 +TARGETS_new += new-$1 +HELP_new-$1 := $$(TEMPLATING_HELP_$1) +new-$1: $$(TEMPLATING_OUTPUT_$1) | _check-new-$1 + +$$(NAME)/%:$$(NAME)/%.m4 + @mkdir -p $$(dir $$@) + $$(TEMPLATING_M4_$1) -D__FILENAME__=$$(notdir $$@) -D__DIRNAME__=$$(dir $$@) $$^ > $$@ + + +define TEMPLATE_RULE_COPY +.INTERMEDIATE: $$2 +$$2:$$1 + @mkdir -p $$(dir $$2) + cp $$1 $$2 +endef + +.SECONDARY: $$(TEMPLATING_OUTPUT_$1) + +$$(foreach \ + INDEX, \ + $$(TEMPLATING_INDEX_$1), \ + $$(eval \ + $$(call \ + TEMPLATE_RULE_COPY, \ + $$(word $$(INDEX), $$(TEMPLATING_INPUT_$1)), \ + $$(word $$(INDEX), $$(TEMPLATING_COPY_$1)) \ + ) \ + ) \ +) + +endef + +$(foreach \ + TEMPLATE, \ + $(TEMPLATING_CHOICES), \ + $(eval $(call TEMPLATE_NEW_TEMPLATING_TARGET,$(TEMPLATE))) \ +) + +.PHONY:new +TARGETS_common += new +HELP_new := create something new from a template +DOCKER_NO += new +new: | help-new + +endif From 0c6627b1370bff8285d62d9f890161912434fd30 Mon Sep 17 00:00:00 2001 From: drechsler Date: Thu, 19 Nov 2020 23:24:18 +0100 Subject: [PATCH 05/12] removed warning print in help.mk --- .make/help.mk | 1 - 1 file changed, 1 deletion(-) diff --git a/.make/help.mk b/.make/help.mk index 9ef4ec4..3fa1492 100644 --- a/.make/help.mk +++ b/.make/help.mk @@ -80,7 +80,6 @@ help-%: @$(foreach \ group, \ $(VARIABLES_$(@:help-%=%)), \ - $(warning $(group)) \ [ -n "$(VARIABLES_$(group))" ] && echo -e "\n--- $(HELP_$(group)) ---" || true; \ $(foreach \ variable, \ From 42b78e19a03db04d9d9660397c3e6c5485e3510b Mon Sep 17 00:00:00 2001 From: drechsler Date: Fri, 20 Nov 2020 10:11:18 +0100 Subject: [PATCH 06/12] fixed m4 invoktion --- .make/templating.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.make/templating.mk b/.make/templating.mk index fc71cf9..9bfdb20 100644 --- a/.make/templating.mk +++ b/.make/templating.mk @@ -31,7 +31,7 @@ HELP_new-$1-vars := 'new-$1' template options TEMPLATING_INPUT_$1 := $$(foreach PATH,$$(TEMPLATING_PATH_$1),$$(shell find $$(PATH) -type f)) TEMPLATING_FILES_$1 := $$(foreach PATH,$$(TEMPLATING_PATH_$1), $$(shell find $$(PATH) -type f -printf '%P\n') ) TEMPLATING_INDEX_$1 := $$(shell for i in {1..$$(words $$(TEMPLATING_FILES_$1))}; do echo $$$$i; done) -TEMPLATING_M4_$1 := m4 $$(foreach DEFINE,$$(TEMPLATING_VARS_$1),-D__$$(DEFINE)__=$$($$(DEFINE))) +TEMPLATING_M4_$1 := m4 $$(foreach DEFINE,$$(VARIABLES_TEMPLATING_COMMON) $$(TEMPLATING_VARS_$1),-D__$$(DEFINE)__=$$($$(DEFINE))) TEMPLATING_COPY_$1 := $$(shell echo '$$(addprefix $$(NAME)/, $$(TEMPLATING_FILES_$1))' | $$(TEMPLATING_M4_$1)) TEMPLATING_OUTPUT_$1 := $$(patsubst $$(TEMPLATING_SUFFIXES),%,$$(TEMPLATING_COPY_$1)) From 16dcf04690bbfb20caf5c73f90328092c85392a7 Mon Sep 17 00:00:00 2001 From: drechsler Date: Fri, 20 Nov 2020 10:12:24 +0100 Subject: [PATCH 07/12] made error printing more readable --- .make/templating.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.make/templating.mk b/.make/templating.mk index 9bfdb20..a1c6b29 100644 --- a/.make/templating.mk +++ b/.make/templating.mk @@ -43,7 +43,7 @@ _check-new-$1: $$(if \ $$($$(VAR)), \ , \ - $$(error option $$(VAR) is missing. please specify via '$$(VAR)='. see 'make help-new' for more information) \ + $$(info ERROR: option $$(VAR) is missing. please specify via '$$(VAR)='. see 'make help-new' for more information) $$(error ) \ ) \ ) From 10de7daac586ba465cd98a66a84f814f3ccb1b29 Mon Sep 17 00:00:00 2001 From: drechsler Date: Sat, 21 Nov 2020 11:52:58 +0100 Subject: [PATCH 08/12] modified part template for new templating mechanism --- parts/.template.mk | 8 ++++++++ parts/.template/{ => part}/Makefile | 0 parts/.template/{ => part}/README.md | 0 parts/.template/{v1 => part/__VERSION__}/Makefile | 0 parts/.template/{v1 => part/__VERSION__}/README.md | 0 parts/.template/{v1 => part/__VERSION__}/code/.gitignore | 0 parts/.template/{v1 => part/__VERSION__}/code/Makefile | 0 parts/.template/{v1 => part/__VERSION__}/code/README.md | 0 parts/.template/{v1 => part/__VERSION__}/code/main.c | 0 parts/.template/{v1 => part/__VERSION__}/docs/README.md | 0 parts/.template/{v1 => part/__VERSION__}/kicad/README.md | 0 .../__VERSION__}/kicad/simulation/simulation-cache.lib | 0 .../__VERSION__}/kicad/simulation/simulation.kicad_pcb | 0 .../__VERSION__}/kicad/simulation/simulation.pro | 0 .../__VERSION__}/kicad/simulation/simulation.sch | 0 .../__VERSION__}/kicad/simulation/simulation.sch-bak | 0 .../kicad/voltage_divider/voltage_divider-cache.lib | 0 .../kicad/voltage_divider/voltage_divider.kicad_pcb | 0 .../kicad/voltage_divider/voltage_divider.pro | 0 .../kicad/voltage_divider/voltage_divider.sch | 0 .../kicad/voltage_divider/voltage_divider.sch-bak | 0 .../{v1 => part/__VERSION__}/mechanics/README.md | 0 .../{v1 => part/__VERSION__}/requirements/README.md | 0 .../.template/{v1 => part/__VERSION__}/scripts/README.md | 0 24 files changed, 8 insertions(+) create mode 100644 parts/.template.mk rename parts/.template/{ => part}/Makefile (100%) rename parts/.template/{ => part}/README.md (100%) rename parts/.template/{v1 => part/__VERSION__}/Makefile (100%) rename parts/.template/{v1 => part/__VERSION__}/README.md (100%) rename parts/.template/{v1 => part/__VERSION__}/code/.gitignore (100%) rename parts/.template/{v1 => part/__VERSION__}/code/Makefile (100%) rename parts/.template/{v1 => part/__VERSION__}/code/README.md (100%) rename parts/.template/{v1 => part/__VERSION__}/code/main.c (100%) rename parts/.template/{v1 => part/__VERSION__}/docs/README.md (100%) rename parts/.template/{v1 => part/__VERSION__}/kicad/README.md (100%) rename parts/.template/{v1 => part/__VERSION__}/kicad/simulation/simulation-cache.lib (100%) rename parts/.template/{v1 => part/__VERSION__}/kicad/simulation/simulation.kicad_pcb (100%) rename parts/.template/{v1 => part/__VERSION__}/kicad/simulation/simulation.pro (100%) rename parts/.template/{v1 => part/__VERSION__}/kicad/simulation/simulation.sch (100%) rename parts/.template/{v1 => part/__VERSION__}/kicad/simulation/simulation.sch-bak (100%) rename parts/.template/{v1 => part/__VERSION__}/kicad/voltage_divider/voltage_divider-cache.lib (100%) rename parts/.template/{v1 => part/__VERSION__}/kicad/voltage_divider/voltage_divider.kicad_pcb (100%) rename parts/.template/{v1 => part/__VERSION__}/kicad/voltage_divider/voltage_divider.pro (100%) rename parts/.template/{v1 => part/__VERSION__}/kicad/voltage_divider/voltage_divider.sch (100%) rename parts/.template/{v1 => part/__VERSION__}/kicad/voltage_divider/voltage_divider.sch-bak (100%) rename parts/.template/{v1 => part/__VERSION__}/mechanics/README.md (100%) rename parts/.template/{v1 => part/__VERSION__}/requirements/README.md (100%) rename parts/.template/{v1 => part/__VERSION__}/scripts/README.md (100%) diff --git a/parts/.template.mk b/parts/.template.mk new file mode 100644 index 0000000..0b732aa --- /dev/null +++ b/parts/.template.mk @@ -0,0 +1,8 @@ + +TEMPLATING_CHOICES += part +TEMPLATING_HELP_part := create a new part +TEMPLATING_PATH_part += .template/part +TEMPLATING_VARS_part += VERSION + +VERSION := v1 +HELP_VERSION := Sets the first version \ No newline at end of file diff --git a/parts/.template/Makefile b/parts/.template/part/Makefile similarity index 100% rename from parts/.template/Makefile rename to parts/.template/part/Makefile diff --git a/parts/.template/README.md b/parts/.template/part/README.md similarity index 100% rename from parts/.template/README.md rename to parts/.template/part/README.md diff --git a/parts/.template/v1/Makefile b/parts/.template/part/__VERSION__/Makefile similarity index 100% rename from parts/.template/v1/Makefile rename to parts/.template/part/__VERSION__/Makefile diff --git a/parts/.template/v1/README.md b/parts/.template/part/__VERSION__/README.md similarity index 100% rename from parts/.template/v1/README.md rename to parts/.template/part/__VERSION__/README.md diff --git a/parts/.template/v1/code/.gitignore b/parts/.template/part/__VERSION__/code/.gitignore similarity index 100% rename from parts/.template/v1/code/.gitignore rename to parts/.template/part/__VERSION__/code/.gitignore diff --git a/parts/.template/v1/code/Makefile b/parts/.template/part/__VERSION__/code/Makefile similarity index 100% rename from parts/.template/v1/code/Makefile rename to parts/.template/part/__VERSION__/code/Makefile diff --git a/parts/.template/v1/code/README.md b/parts/.template/part/__VERSION__/code/README.md similarity index 100% rename from parts/.template/v1/code/README.md rename to parts/.template/part/__VERSION__/code/README.md diff --git a/parts/.template/v1/code/main.c b/parts/.template/part/__VERSION__/code/main.c similarity index 100% rename from parts/.template/v1/code/main.c rename to parts/.template/part/__VERSION__/code/main.c diff --git a/parts/.template/v1/docs/README.md b/parts/.template/part/__VERSION__/docs/README.md similarity index 100% rename from parts/.template/v1/docs/README.md rename to parts/.template/part/__VERSION__/docs/README.md diff --git a/parts/.template/v1/kicad/README.md b/parts/.template/part/__VERSION__/kicad/README.md similarity index 100% rename from parts/.template/v1/kicad/README.md rename to parts/.template/part/__VERSION__/kicad/README.md diff --git a/parts/.template/v1/kicad/simulation/simulation-cache.lib b/parts/.template/part/__VERSION__/kicad/simulation/simulation-cache.lib similarity index 100% rename from parts/.template/v1/kicad/simulation/simulation-cache.lib rename to parts/.template/part/__VERSION__/kicad/simulation/simulation-cache.lib diff --git a/parts/.template/v1/kicad/simulation/simulation.kicad_pcb b/parts/.template/part/__VERSION__/kicad/simulation/simulation.kicad_pcb similarity index 100% rename from parts/.template/v1/kicad/simulation/simulation.kicad_pcb rename to parts/.template/part/__VERSION__/kicad/simulation/simulation.kicad_pcb diff --git a/parts/.template/v1/kicad/simulation/simulation.pro b/parts/.template/part/__VERSION__/kicad/simulation/simulation.pro similarity index 100% rename from parts/.template/v1/kicad/simulation/simulation.pro rename to parts/.template/part/__VERSION__/kicad/simulation/simulation.pro diff --git a/parts/.template/v1/kicad/simulation/simulation.sch b/parts/.template/part/__VERSION__/kicad/simulation/simulation.sch similarity index 100% rename from parts/.template/v1/kicad/simulation/simulation.sch rename to parts/.template/part/__VERSION__/kicad/simulation/simulation.sch diff --git a/parts/.template/v1/kicad/simulation/simulation.sch-bak b/parts/.template/part/__VERSION__/kicad/simulation/simulation.sch-bak similarity index 100% rename from parts/.template/v1/kicad/simulation/simulation.sch-bak rename to parts/.template/part/__VERSION__/kicad/simulation/simulation.sch-bak diff --git a/parts/.template/v1/kicad/voltage_divider/voltage_divider-cache.lib b/parts/.template/part/__VERSION__/kicad/voltage_divider/voltage_divider-cache.lib similarity index 100% rename from parts/.template/v1/kicad/voltage_divider/voltage_divider-cache.lib rename to parts/.template/part/__VERSION__/kicad/voltage_divider/voltage_divider-cache.lib diff --git a/parts/.template/v1/kicad/voltage_divider/voltage_divider.kicad_pcb b/parts/.template/part/__VERSION__/kicad/voltage_divider/voltage_divider.kicad_pcb similarity index 100% rename from parts/.template/v1/kicad/voltage_divider/voltage_divider.kicad_pcb rename to parts/.template/part/__VERSION__/kicad/voltage_divider/voltage_divider.kicad_pcb diff --git a/parts/.template/v1/kicad/voltage_divider/voltage_divider.pro b/parts/.template/part/__VERSION__/kicad/voltage_divider/voltage_divider.pro similarity index 100% rename from parts/.template/v1/kicad/voltage_divider/voltage_divider.pro rename to parts/.template/part/__VERSION__/kicad/voltage_divider/voltage_divider.pro diff --git a/parts/.template/v1/kicad/voltage_divider/voltage_divider.sch b/parts/.template/part/__VERSION__/kicad/voltage_divider/voltage_divider.sch similarity index 100% rename from parts/.template/v1/kicad/voltage_divider/voltage_divider.sch rename to parts/.template/part/__VERSION__/kicad/voltage_divider/voltage_divider.sch diff --git a/parts/.template/v1/kicad/voltage_divider/voltage_divider.sch-bak b/parts/.template/part/__VERSION__/kicad/voltage_divider/voltage_divider.sch-bak similarity index 100% rename from parts/.template/v1/kicad/voltage_divider/voltage_divider.sch-bak rename to parts/.template/part/__VERSION__/kicad/voltage_divider/voltage_divider.sch-bak diff --git a/parts/.template/v1/mechanics/README.md b/parts/.template/part/__VERSION__/mechanics/README.md similarity index 100% rename from parts/.template/v1/mechanics/README.md rename to parts/.template/part/__VERSION__/mechanics/README.md diff --git a/parts/.template/v1/requirements/README.md b/parts/.template/part/__VERSION__/requirements/README.md similarity index 100% rename from parts/.template/v1/requirements/README.md rename to parts/.template/part/__VERSION__/requirements/README.md diff --git a/parts/.template/v1/scripts/README.md b/parts/.template/part/__VERSION__/scripts/README.md similarity index 100% rename from parts/.template/v1/scripts/README.md rename to parts/.template/part/__VERSION__/scripts/README.md From 28b50063ce72af54d4d813ec3db49cd7f087f0d7 Mon Sep 17 00:00:00 2001 From: drechsler Date: Tue, 24 Nov 2020 22:59:48 +0100 Subject: [PATCH 09/12] added envsubst templating --- .make/templating.mk | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/.make/templating.mk b/.make/templating.mk index a1c6b29..a4979bf 100644 --- a/.make/templating.mk +++ b/.make/templating.mk @@ -16,7 +16,8 @@ VARIABLES_TEMPLATING_COMMON += NAME HELP_NAME := how to rename the template NAME ?= #empty -TEMPLATING_SUFFIXES += %.m4 +TEMPLATING_SUFFIXES += m4 +TEMPLATING_SUFFIXES += envsubst define TEMPLATE_NEW_TEMPLATING_TARGET @@ -27,13 +28,15 @@ VARIABLES_new += new-$1-vars VARIABLES_new-$1-vars := $$(TEMPLATING_VARS_$1) HELP_new-$1-vars := 'new-$1' template options +TEMPLATING_M4_$1 := $$(foreach DEFINE,$$(VARIABLES_TEMPLATING_COMMON) $$(TEMPLATING_VARS_$1),-D__$$(DEFINE)__=$$($$(DEFINE))) +TEMPLATING_ENVSUBST_$1 := $$(foreach DEFINE,$$(VARIABLES_TEMPLATING_COMMON) $$(TEMPLATING_VARS_$1),$$$${$$(DEFINE)} ) TEMPLATING_INPUT_$1 := $$(foreach PATH,$$(TEMPLATING_PATH_$1),$$(shell find $$(PATH) -type f)) TEMPLATING_FILES_$1 := $$(foreach PATH,$$(TEMPLATING_PATH_$1), $$(shell find $$(PATH) -type f -printf '%P\n') ) TEMPLATING_INDEX_$1 := $$(shell for i in {1..$$(words $$(TEMPLATING_FILES_$1))}; do echo $$$$i; done) -TEMPLATING_M4_$1 := m4 $$(foreach DEFINE,$$(VARIABLES_TEMPLATING_COMMON) $$(TEMPLATING_VARS_$1),-D__$$(DEFINE)__=$$($$(DEFINE))) -TEMPLATING_COPY_$1 := $$(shell echo '$$(addprefix $$(NAME)/, $$(TEMPLATING_FILES_$1))' | $$(TEMPLATING_M4_$1)) -TEMPLATING_OUTPUT_$1 := $$(patsubst $$(TEMPLATING_SUFFIXES),%,$$(TEMPLATING_COPY_$1)) +TEMPLATING_COPY_$1 := $$(shell echo '$$(addprefix $$(NAME)/, $$(TEMPLATING_FILES_$1))' | m4 $$(TEMPLATING_M4_$1)) +TEMPLATING_OUTPUT_$1 := $$(shell echo "$$(TEMPLATING_COPY_$1)" | sed $$(foreach SUFFIX,$$(TEMPLATING_SUFFIXES), -e 's/\.$$(SUFFIX)\s/ /g')) + .PHONY: _check-new-$1 _check-new-$1: @@ -54,7 +57,12 @@ new-$1: $$(TEMPLATING_OUTPUT_$1) | _check-new-$1 $$(NAME)/%:$$(NAME)/%.m4 @mkdir -p $$(dir $$@) - $$(TEMPLATING_M4_$1) -D__FILENAME__=$$(notdir $$@) -D__DIRNAME__=$$(dir $$@) $$^ > $$@ + m4 $$(TEMPLATING_M4_$1) -D__FILENAME__=$$(notdir $$@) -D__DIRNAME__=$$(dir $$@) $$^ > $$@ + +$$(NAME)/%:$$(NAME)/%.envsubst + @mkdir -p $$(dir $$@) + FILENAME=$$(notdir $$@) DIRNAME=$$(dir $$@) $$(foreach DEFINE,$$(VARIABLES_TEMPLATING_COMMON) $$(TEMPLATING_VARS_$1),$$(DEFINE)=$$($$(DEFINE))) \ + envsubst '$$(TEMPLATING_ENVSUBST_$1) $$$${FILENAME} $$$${DIRNAME}' < $$^ > $$@ define TEMPLATE_RULE_COPY From 3f04cfbe3eacca0897d930d9f389f843713d36b0 Mon Sep 17 00:00:00 2001 From: drechsler Date: Tue, 24 Nov 2020 23:26:00 +0100 Subject: [PATCH 10/12] fixed sed pattern --- .make/templating.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.make/templating.mk b/.make/templating.mk index a4979bf..5535538 100644 --- a/.make/templating.mk +++ b/.make/templating.mk @@ -35,7 +35,7 @@ TEMPLATING_INPUT_$1 := $$(foreach PATH,$$(TEMPLATING_PATH_$1),$$(shell find $$( TEMPLATING_FILES_$1 := $$(foreach PATH,$$(TEMPLATING_PATH_$1), $$(shell find $$(PATH) -type f -printf '%P\n') ) TEMPLATING_INDEX_$1 := $$(shell for i in {1..$$(words $$(TEMPLATING_FILES_$1))}; do echo $$$$i; done) TEMPLATING_COPY_$1 := $$(shell echo '$$(addprefix $$(NAME)/, $$(TEMPLATING_FILES_$1))' | m4 $$(TEMPLATING_M4_$1)) -TEMPLATING_OUTPUT_$1 := $$(shell echo "$$(TEMPLATING_COPY_$1)" | sed $$(foreach SUFFIX,$$(TEMPLATING_SUFFIXES), -e 's/\.$$(SUFFIX)\s/ /g')) +TEMPLATING_OUTPUT_$1 := $$(shell echo "$$(TEMPLATING_COPY_$1) " | sed $$(foreach SUFFIX,$$(TEMPLATING_SUFFIXES), -e 's/\.$$(SUFFIX)\s/ /g')) .PHONY: _check-new-$1 From 8048e5dd8d1ce44e002d848f7f9888315ea0f122 Mon Sep 17 00:00:00 2001 From: drechsler Date: Tue, 24 Nov 2020 23:44:04 +0100 Subject: [PATCH 11/12] fix sed pattern --- .make/templating.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.make/templating.mk b/.make/templating.mk index 5535538..f363bfc 100644 --- a/.make/templating.mk +++ b/.make/templating.mk @@ -35,7 +35,7 @@ TEMPLATING_INPUT_$1 := $$(foreach PATH,$$(TEMPLATING_PATH_$1),$$(shell find $$( TEMPLATING_FILES_$1 := $$(foreach PATH,$$(TEMPLATING_PATH_$1), $$(shell find $$(PATH) -type f -printf '%P\n') ) TEMPLATING_INDEX_$1 := $$(shell for i in {1..$$(words $$(TEMPLATING_FILES_$1))}; do echo $$$$i; done) TEMPLATING_COPY_$1 := $$(shell echo '$$(addprefix $$(NAME)/, $$(TEMPLATING_FILES_$1))' | m4 $$(TEMPLATING_M4_$1)) -TEMPLATING_OUTPUT_$1 := $$(shell echo "$$(TEMPLATING_COPY_$1) " | sed $$(foreach SUFFIX,$$(TEMPLATING_SUFFIXES), -e 's/\.$$(SUFFIX)\s/ /g')) +TEMPLATING_OUTPUT_$1 := $$(shell echo "$$(TEMPLATING_COPY_$1) " | sed -E $$(foreach SUFFIX,$$(TEMPLATING_SUFFIXES), -e 's/([^\S]+)\.$$(SUFFIX)/\1/g')) .PHONY: _check-new-$1 From d3645adf072a195e710ca2963f2e612f11eebfae Mon Sep 17 00:00:00 2001 From: drechsler Date: Fri, 27 Nov 2020 00:22:05 +0100 Subject: [PATCH 12/12] replaced for loop bash construct with seq --- .make/templating.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.make/templating.mk b/.make/templating.mk index f363bfc..86fbba6 100644 --- a/.make/templating.mk +++ b/.make/templating.mk @@ -33,7 +33,7 @@ TEMPLATING_ENVSUBST_$1 := $$(foreach DEFINE,$$(VARIABLES_TEMPLATING_COMMON) $$(T TEMPLATING_INPUT_$1 := $$(foreach PATH,$$(TEMPLATING_PATH_$1),$$(shell find $$(PATH) -type f)) TEMPLATING_FILES_$1 := $$(foreach PATH,$$(TEMPLATING_PATH_$1), $$(shell find $$(PATH) -type f -printf '%P\n') ) -TEMPLATING_INDEX_$1 := $$(shell for i in {1..$$(words $$(TEMPLATING_FILES_$1))}; do echo $$$$i; done) +TEMPLATING_INDEX_$1 := $$(shell for i in $$$$(seq 1 $$(words $$(TEMPLATING_FILES_$1))); do echo $$$$i; done) TEMPLATING_COPY_$1 := $$(shell echo '$$(addprefix $$(NAME)/, $$(TEMPLATING_FILES_$1))' | m4 $$(TEMPLATING_M4_$1)) TEMPLATING_OUTPUT_$1 := $$(shell echo "$$(TEMPLATING_COPY_$1) " | sed -E $$(foreach SUFFIX,$$(TEMPLATING_SUFFIXES), -e 's/([^\S]+)\.$$(SUFFIX)/\1/g'))