diff --git a/.github/workflows/registry-build-publish.yaml b/.github/workflows/registry-build-publish.yaml index 0bc8c77..fa269d1 100644 --- a/.github/workflows/registry-build-publish.yaml +++ b/.github/workflows/registry-build-publish.yaml @@ -45,6 +45,7 @@ jobs: uses: docker/build-push-action@7f9d37fa544684fb73bfe4835ed7214c255ce02b with: context: . + target: production push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/release-arduino-lib-versions.yaml b/.github/workflows/release-arduino-lib-versions.yaml new file mode 100644 index 0000000..a75a737 --- /dev/null +++ b/.github/workflows/release-arduino-lib-versions.yaml @@ -0,0 +1,46 @@ +name: Update Release Body with Arduino Libraries + +on: + release: + types: [published] + +jobs: + update-release-body: + runs-on: ubuntu-latest + + steps: + # Checkout the repository + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install jq + run: sudo apt-get install jq + + - name: Build docker container + run: docker build -t sketches --no-cache . + + # Generate the libraries list + - name: Generate libraries list + id: generate_libraries_list + run: docker run sketches arduino-cli lib list --all > libraries.txt + + # Update the release body + - name: Update Release Body + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Fetch the release details + RELEASE_URL=$(jq -r '.release.url' "${GITHUB_EVENT_PATH}") + RELEASE_BODY=$(jq -r '.release.body' "${GITHUB_EVENT_PATH}") + + LIBRARIES_FILE=$(cat libraries.txt) + + # Append the generated text with a heading and escape special characters + NEW_BODY=$(echo -e "${RELEASE_BODY}\n\n## Libraries\n\n\`\`\`\n${LIBRARIES_FILE}\n\`\`\`" | jq -Rs .) + + # Update the release body using GitHub API + curl -X PATCH \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github.v3+json" \ + "${RELEASE_URL}" \ + -d "{\"body\":${NEW_BODY}}" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2419e0c..394bd28 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,9 +4,7 @@ name: Tests # Controls when the action will run. on: - # Triggers the workflow on push or pull request events but only for the master branch - push: - branches: [main] + # Triggers the workflow on pull request events but only for the main branch pull_request: branches: [main] @@ -26,7 +24,13 @@ jobs: - uses: actions/checkout@v2 - name: Build docker test container - run: docker build -t sketches-test -f Dockerfile.test --no-cache . + run: docker build -t sketches-test --target test --no-cache . - name: Run docker test container - run: docker run sketches-test + run: docker run -v ${{ github.workspace }}/ctrf:/app/ctrf sketches-test + + - name: Run CTRF annotations + run: npx github-actions-ctrf pull-request ctrf/ctrf-report.json + if: always() + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index a2688e6..bbc97b3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ node_modules yarn-error.log *.bin *.hex + +ctrf \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 4742d7e..3914e01 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,29 +1,25 @@ -FROM node:17 - -ENV ARDUINO_CLI_VERSION=0.35.3 \ - SENSEBOXCORE_VERSION=2.0.0 \ - ARDUINO_SAMD_VERSION=1.8.13 \ - ARDUINO_AVR_VERSION=1.8.5 \ - ESP32_VERSION=2.0.17 \ - SENSEBOXCORE_URL=https://raw.githubusercontent.com/mariopesch/senseBoxMCU-core/master/package_sensebox_index.json \ - ESP32CORE_URL=https://espressif.github.io/arduino-esp32/package_esp32_index.json \ - SSD1306_PLOT_LIBRARY_URL=https://github.com/sensebox/SSD1306-Plot-Library/archive/refs/tags/v1.1.0.zip \ - SENSEBOX_LIBWEB_URL=https://github.com/sensebox/sensebox-libweb/archive/refs/heads/master.zip \ - SDS011_LIBRARY_URL=https://github.com/sensebox/SDS011-select-serial/archive/refs/heads/master.zip \ - RTC_LIBRARY_URL=https://github.com/sensebox/RV8523-RTC-Arduino-Library/archive/refs/heads/main.zip \ - BMX055_LIBRARY_URL=https://github.com/sensebox/BMX055-Arduino-Library/archive/refs/heads/main.zip \ - LTR329_LIBRARY_URL=https://github.com/sensebox/LTR329-Lightsensor-Arduino-Library/archive/refs/heads/main.zip \ - SDS011S_LIBRARY_URL=https://github.com/sensebox/SDS011-select-serial/archive/refs/heads/master.zip \ - VEML6070_LIBRARY_URL=https://github.com/sensebox/VEML6070-UV-Arduino-Library/archive/refs/heads/main.zip \ - AMS5915_LIBRARY_URL=https://github.com/bolderflight/ams5915/archive/refs/heads/main.zip - -RUN apt-get update && apt-get install -y xz-utils unzip wget - -RUN curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=/usr/local/bin sh -s ${ARDUINO_CLI_VERSION} +FROM node:22-alpine AS base + +ENV ARDUINO_CLI_VERSION=1.1.0 +ENV SENSEBOXCORE_VERSION=2.0.0 +ENV ARDUINO_SAMD_VERSION=1.8.13 +ENV ARDUINO_AVR_VERSION=1.8.5 +ENV ESP32_VERSION=2.0.17 +ENV SENSEBOXCORE_URL=https://raw.githubusercontent.com/mariopesch/senseBoxMCU-core/master/package_sensebox_index.json +ENV ESP32CORE_URL=https://espressif.github.io/arduino-esp32/package_esp32_index.json + +RUN apk update +RUN apk add curl +RUN apk add libc6-compat +RUN apk add bash +RUN apk add python3 +RUN apk add py3-pyserial + +RUN curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh -s ${ARDUINO_CLI_VERSION} RUN arduino-cli config init -# aloow unsafe sources (zip, git) +# allow unsafe sources (zip, git) RUN arduino-cli config set library.enable_unsafe_install true # update arduino-cli @@ -38,31 +34,9 @@ RUN curl -o /root/.arduino15/package_sensebox_index.json ${SENSEBOXCORE_URL} RUN arduino-cli --additional-urls ${SENSEBOXCORE_URL} core install sensebox:samd # install ESP32 -RUN apt-get install -y python3-pip -RUN pip install pyserial RUN curl -o /root/.arduino15/package_esp32_index.json ${ESP32CORE_URL} RUN arduino-cli --additional-urls ${ESP32CORE_URL} core install esp32:esp32@${ESP32_VERSION} - -RUN wget -O ssd1306_plot_library.zip $SSD1306_PLOT_LIBRARY_URL \ - && arduino-cli lib install --zip-path ssd1306_plot_library.zip \ - && wget -O sensebox_libweb.zip $SENSEBOX_LIBWEB_URL \ - && arduino-cli lib install --zip-path sensebox_libweb.zip \ - && wget -O sds011-select-serial.zip $SDS011_LIBRARY_URL \ - && arduino-cli lib install --zip-path sds011-select-serial.zip \ - && wget -O rtc_library.zip $RTC_LIBRARY_URL \ - && arduino-cli lib install --zip-path rtc_library.zip \ - && wget -O bmx055_library.zip $BMX055_LIBRARY_URL \ - && arduino-cli lib install --zip-path bmx055_library.zip \ - && wget -O ltr329_library.zip $LTR329_LIBRARY_URL \ - && arduino-cli lib install --zip-path ltr329_library.zip \ - && wget -O sds011_select_library.zip $SDS011S_LIBRARY_URL \ - && arduino-cli lib install --zip-path sds011_select_library.zip \ - && wget -O veml6070_library.zip $VEML6070_LIBRARY_URL \ - && arduino-cli lib install --zip-path veml6070_library.zip \ - && wget -O ams5915_library.zip $AMS5915_LIBRARY_URL \ - && arduino-cli lib install --zip-path ams5915_library.zip - # install Libraries with arduino-cli RUN arduino-cli lib install "Ethernet" RUN arduino-cli lib install "ArduinoJson" @@ -109,20 +83,36 @@ RUN arduino-cli lib install "NeoGPS" RUN arduino-cli lib install "Adafruit NeoMatrix" RUN arduino-cli lib install "Arduino Low Power" RUN arduino-cli lib install "Adafruit seesaw Library" - +RUN arduino-cli lib install --git-url https://github.com/sensebox/SSD1306-Plot-Library +RUN arduino-cli lib install --git-url https://github.com/sensebox/sensebox-libweb +RUN arduino-cli lib install --git-url https://github.com/sensebox/SDS011-select-serial +RUN arduino-cli lib install --git-url https://github.com/sensebox/RV8523-RTC-Arduino-Library +RUN arduino-cli lib install --git-url https://github.com/sensebox/BMX055-Arduino-Library +RUN arduino-cli lib install --git-url https://github.com/sensebox/LTR329-Lightsensor-Arduino-Library +RUN arduino-cli lib install --git-url https://github.com/sensebox/VEML6070-UV-Arduino-Library +RUN arduino-cli lib install --git-url https://github.com/bolderflight/ams5915 WORKDIR /app -ENV NODE_ENV=production - COPY package.json /app COPY yarn.lock /app -RUN yarn install --pure-lockfile --production +# test stage +FROM base AS test +ENV NODE_ENV=test +RUN yarn install --pure-lockfile +COPY src /app/src +COPY test /app/test +COPY mocha-reporters.json /app +CMD ["yarn","test"] +# production stage +FROM base AS production +ENV NODE_ENV=production +RUN yarn install --pure-lockfile --production COPY src /app/src COPY splash.h ../root/Arduino/libraries/Adafruit_SSD1306/splash.h # COPY platform.txt /app/src/arduino-ide/packages/arduino/hardware/samd/1.8.11 -CMD ["yarn","start"] +CMD ["yarn","start"] \ No newline at end of file diff --git a/Dockerfile.test b/Dockerfile.test deleted file mode 100644 index 4cdbfbf..0000000 --- a/Dockerfile.test +++ /dev/null @@ -1,120 +0,0 @@ -FROM node:17 - -ENV ARDUINO_CLI_VERSION=0.34.2 \ - SENSEBOXCORE_VERSION=2.0.0 \ - ARDUINO_SAMD_VERSION=1.8.13 \ - ARDUINO_AVR_VERSION=1.8.5 \ - ESP32_VERSION=2.0.17 \ - SENSEBOXCORE_URL=https://raw.githubusercontent.com/mariopesch/senseBoxMCU-core/master/package_sensebox_index.json \ - ESP32CORE_URL=https://espressif.github.io/arduino-esp32/package_esp32_index.json \ - SSD1306_PLOT_LIBRARY_URL=https://github.com/sensebox/SSD1306-Plot-Library/archive/refs/tags/v1.0.0.zip \ - SENSEBOX_LIBWEB_URL=https://github.com/sensebox/sensebox-libweb/archive/refs/heads/master.zip \ - SDS011_LIBRARY_URL=https://github.com/sensebox/SDS011-select-serial/archive/refs/heads/master.zip \ - RTC_LIBRARY_URL=https://github.com/sensebox/RV8523-RTC-Arduino-Library/archive/refs/heads/main.zip \ - BMX055_LIBRARY_URL=https://github.com/sensebox/BMX055-Arduino-Library/archive/refs/heads/main.zip \ - LTR329_LIBRARY_URL=https://github.com/sensebox/LTR329-Lightsensor-Arduino-Library/archive/refs/heads/main.zip \ - SDS011S_LIBRARY_URL=https://github.com/sensebox/SDS011-select-serial/archive/refs/heads/master.zip \ - VEML6070_LIBRARY_URL=https://github.com/sensebox/VEML6070-UV-Arduino-Library/archive/refs/heads/main.zip \ - AMS5915_LIBRARY_URL=https://github.com/bolderflight/ams5915/archive/refs/heads/main.zip - -RUN apt-get update && apt-get install -y xz-utils unzip wget - -RUN curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=/usr/local/bin sh -s ${ARDUINO_CLI_VERSION} - -RUN arduino-cli config init - -# aloow unsafe sources (zip, git) -RUN arduino-cli config set library.enable_unsafe_install true - -# update arduino-cli -RUN arduino-cli core update-index - -RUN arduino-cli core install arduino:avr - -RUN arduino-cli core install arduino:samd@${ARDUINO_SAMD_VERSION} - -# install arduino stuff for senseBox V2 -RUN curl -o /root/.arduino15/package_sensebox_index.json ${SENSEBOXCORE_URL} -RUN arduino-cli --additional-urls ${SENSEBOXCORE_URL} core install sensebox:samd - -# install ESP32 -RUN apt-get install -y python3-pip -RUN pip install pyserial -RUN curl -o /root/.arduino15/package_esp32_index.json ${ESP32CORE_URL} -RUN arduino-cli --additional-urls ${ESP32CORE_URL} core install esp32:esp32@${ESP32_VERSION} - - -RUN wget -O ssd1306_plot_library.zip $SSD1306_PLOT_LIBRARY_URL \ - && arduino-cli lib install --zip-path ssd1306_plot_library.zip \ - && wget -O sensebox_libweb.zip $SENSEBOX_LIBWEB_URL \ - && arduino-cli lib install --zip-path sensebox_libweb.zip \ - && wget -O sds011-select-serial.zip $SDS011_LIBRARY_URL \ - && arduino-cli lib install --zip-path sds011-select-serial.zip \ - && wget -O rtc_library.zip $RTC_LIBRARY_URL \ - && arduino-cli lib install --zip-path rtc_library.zip \ - && wget -O bmx055_library.zip $BMX055_LIBRARY_URL \ - && arduino-cli lib install --zip-path bmx055_library.zip \ - && wget -O ltr329_library.zip $LTR329_LIBRARY_URL \ - && arduino-cli lib install --zip-path ltr329_library.zip \ - && wget -O sds011_select_library.zip $SDS011S_LIBRARY_URL \ - && arduino-cli lib install --zip-path sds011_select_library.zip \ - && wget -O veml6070_library.zip $VEML6070_LIBRARY_URL \ - && arduino-cli lib install --zip-path veml6070_library.zip \ - && wget -O ams5915_library.zip $AMS5915_LIBRARY_URL \ - && arduino-cli lib install --zip-path ams5915_library.zip - -# install Libraries with arduino-cli -RUN arduino-cli lib install "Ethernet" -RUN arduino-cli lib install "ArduinoJson" -RUN arduino-cli lib install "Adafruit HDC1000 Library" -RUN arduino-cli lib install "Adafruit BME280 Library" -RUN arduino-cli lib install "Adafruit BMP280 Library" -RUN arduino-cli lib install "Adafruit BME680 Library" -RUN arduino-cli lib install "Adafruit DPS310" -RUN arduino-cli lib install "Adafruit NeoPixel" -RUN arduino-cli lib install "Adafruit SSD1306" -RUN arduino-cli lib install "Adafruit GFX Library" -RUN arduino-cli lib install "Adafruit MQTT Library" -RUN arduino-cli lib install "Adafruit BusIO" -RUN arduino-cli lib install "Adafruit SleepyDog Library" -RUN arduino-cli lib install "Adafruit MPU6050" -RUN arduino-cli lib install "DallasTemperature" -RUN arduino-cli lib install "ArduinoBearSSL" -RUN arduino-cli lib install "ArduinoECCX08" -RUN arduino-cli lib install "SparkFun SCD30 Arduino Library" -RUN arduino-cli lib install "SparkFun u-blox GNSS Arduino Library" -RUN arduino-cli lib install "NewPing" -RUN arduino-cli lib install "IBM LMIC framework" -RUN arduino-cli lib install "LoRa Serialization" -RUN arduino-cli lib install "CayenneLPP" -RUN arduino-cli lib install "OneWire" -RUN arduino-cli lib install "Nova Fitness Sds dust sensors library" -RUN arduino-cli lib install "JC_Button" -RUN arduino-cli lib install "SD" -RUN arduino-cli lib install "BSEC Software Library" -RUN arduino-cli lib install "TheThingsNetwork" -RUN arduino-cli lib install "NTPClient" -RUN arduino-cli lib install "phyphox BLE" -RUN arduino-cli lib install "UniversalTelegramBot" -RUN arduino-cli lib install "Servo" -RUN arduino-cli lib install "RTCZero" -RUN arduino-cli lib install "sensirion-sps" -RUN arduino-cli lib install "TinyGPSPlus" -RUN arduino-cli lib install "SenseBoxBLE" -RUN arduino-cli lib install "Bolder Flight Systems Unit Conversions" -RUN arduino-cli lib install "HX711" - - -WORKDIR /app - -COPY package.json /app -COPY yarn.lock /app - -RUN yarn -COPY src /app/src -COPY test /app/test -COPY splash.h ../root/Arduino/libraries/Adafruit_SSD1306/splash.h - -# COPY platform.txt /app/src/arduino-ide/packages/arduino/hardware/samd/1.8.11 - -CMD ["yarn","test"] diff --git a/mocha-reporters.json b/mocha-reporters.json new file mode 100644 index 0000000..207223b --- /dev/null +++ b/mocha-reporters.json @@ -0,0 +1,3 @@ +{ + "reporterEnabled": "spec, mocha-ctrf-json-reporter" +} \ No newline at end of file diff --git a/package.json b/package.json index 6187931..1148e1a 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "license": "MIT", "scripts": { "start": "node src/index.js", - "test": "mocha --exit --timeout 20000" + "test": "mocha --exit --timeout 60000 --reporter mocha-multi-reporters --reporter-options configFile=mocha-reporters.json" }, "dependencies": { "body-parser": "^1.18.2", @@ -20,6 +20,8 @@ "devDependencies": { "chai": "^4.2.0", "chai-http": "^4.2.0", - "mocha": "^5.2.0" + "mocha": "^5.2.0", + "mocha-ctrf-json-reporter": "^0.0.6", + "mocha-multi-reporters": "^1.5.1" } } diff --git a/src/index.js b/src/index.js index b35adda..14d011b 100644 --- a/src/index.js +++ b/src/index.js @@ -69,7 +69,7 @@ const errorHandler = function errorHandler (err, req, res, next) { }; const startServer = function startServer () { - app.use(morgan(':date[iso] :res[x-backend-server] :remote-addr :req[x-real-ip] :method :url :response-time[0] :status')); + app.use(morgan(':date[iso] :res[x-backend-server] :remote-addr :req[x-real-ip] :method :url :response-time[0] :status', { skip: (req, res) => process.env.NODE_ENV === 'test' })); app.use(responseTime()); app.use(preflight); app.use(preRequestValidator); diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..f14389e --- /dev/null +++ b/test/index.js @@ -0,0 +1,93 @@ +const chai = require("chai"); +const chaiHttp = require("chai-http"); +const server = require("../src/index"); +const should = chai.should(); + +chai.use(chaiHttp); + +describe("Compiler", () => { + describe("/GET index", () => { + it("it should get the index page and answer with a 404 ", (done) => { + chai + .request(server) + .get("/") + .end((err, res) => { + res.should.have.status(404); + done(); + }); + }); + }); + + describe("/compile", () => { + it("should reject request without board parameter", (done) => { + chai + .request(server) + .post("/compile") + .send({ sketch: 'void setup() {} void loop() {}' }) + .end((err, res) => { + res.should.have.status(422); + res.body.should.have + .property("message") + .eql("Parameters 'sketch' and 'board' are required"); + done(); + }); + }); + + it("should reject request without sketch parameter", (done) => { + chai + .request(server) + .post("/compile") + .send({ board: "sensebox-mcu" }) + .end((err, res) => { + res.should.have.status(422); + res.body.should.have + .property("message") + .eql("Parameters 'sketch' and 'board' are required"); + done(); + }); + }); + + it("should reject request with invalid board", (done) => { + chai + .request(server) + .post("/compile") + .send({ board: "esp8266", sketch: 'void setup() {} void loop() {}' }) + .end((err, res) => { + res.should.have.status(422); + res.body.should.have + .property("message") + .eql("Invalid board parameter. Valid values are: sensebox-mcu,sensebox,sensebox-esp32s2"); + done(); + }); + }); + + it("should reject request with wrong Content-Type", (done) => { + chai + .request(server) + .post("/compile") + .set("Content-Type", "text/plain") + .send("") + .end((err, res) => { + res.should.have.status(415); + res.body.should.have + .property("message") + .eql("Invalid Content-Type. Only application/json Content-Type allowed."); + done(); + }); + }); + + it("should only accept POST request", (done) => { + chai + .request(server) + .get("/compile") + .send({ board: "sensebox-mcu", sketch: 'void setup() {} void loop() {}' }) + .end((err, res) => { + res.should.have.status(405); + res.body.should.have + .property("message") + .eql("Invalid HTTP method. Only POST requests allowed on /compile."); + done(); + }); + }); + }); +}); diff --git a/test/mcu.js b/test/mcu.js new file mode 100644 index 0000000..30c69aa --- /dev/null +++ b/test/mcu.js @@ -0,0 +1,69 @@ +const chai = require("chai"); +const chaiHttp = require("chai-http"); +const server = require("../src/index"); +const should = chai.should(); +const fs = require("fs"); + + +chai.use(chaiHttp); + +describe("Compiler - MCU", () => { + let downloadId_mcu = ""; + + it("should compile an empty sketch for senseBox MCU", (done) => { + const sketch = fs.readFileSync("test/sketches/empty.ino", "utf8"); + + chai + .request(server) + .post("/compile") + .send({ board: "sensebox-mcu", sketch }) + .end((err, res) => { + res.should.have.status(200); + res.body.data.should.have.property("id"); + downloadId_mcu = res.body.data.id; + done(); + }); + }); + + it("should compile a hello world sketch for senseBox MCU", (done) => { + const sketch = fs.readFileSync("test/sketches/hello-world.ino", "utf8"); + + chai + .request(server) + .post("/compile") + .send({ board: "sensebox-mcu", sketch }) + .end((err, res) => { + res.should.have.status(200); + res.body.data.should.have.property("id"); + downloadId_mcu = res.body.data.id; + done(); + }); + }); + + it("should compile a senseBox:home sketch (from openSenseMap) for senseBox MCU", (done) => { + const sketch = fs.readFileSync("test/sketches/mcu/full-home.ino", "utf8"); + + chai + .request(server) + .post("/compile") + .send({ board: "sensebox-mcu", sketch }) + .end((err, res) => { + res.should.have.status(200); + res.body.data.should.have.property("id"); + downloadId_mcu = res.body.data.id; + done(); + }); + }); + + it("should download sketch for senseBox MCU", (done) => { + chai + .request(server) + .get("/download") + .query({ board: "sensebox-mcu", id: downloadId_mcu }) + .end((err, res) => { + res.should.have.status(200); + res.header.should.have.property("content-disposition").eql("attachment; filename=sketch.bin"); + done(); + }); + }); +}); diff --git a/test/mcu_s2.js b/test/mcu_s2.js new file mode 100644 index 0000000..4a156a3 --- /dev/null +++ b/test/mcu_s2.js @@ -0,0 +1,67 @@ +const chai = require("chai"); +const chaiHttp = require("chai-http"); +const server = require("../src/index"); +const fs = require("fs"); +const should = chai.should(); + +chai.use(chaiHttp); + +describe("Compiler - MCU S2 (ESP32S2)", () => { + let downloadId_esp32s2 = ""; + + it("should compile an empty sketch for senseBox MCU-S2 ESP32S2", (done) => { + const sketch = fs.readFileSync("test/sketches/empty.ino", "utf8"); + + chai + .request(server) + .post("/compile") + .send({ board: "sensebox-esp32s2", sketch }) + .end((err, res) => { + res.should.have.status(200); + res.body.data.should.have.property("id"); + downloadId_esp32s2 = res.body.data.id; + done(); + }); + }); + + it("should compile a hello world sketch for senseBox MCU-S2 ESP32S2", (done) => { + const sketch = fs.readFileSync("test/sketches/hello-world.ino", "utf8"); + + chai + .request(server) + .post("/compile") + .send({ board: "sensebox-esp32s2", sketch }) + .end((err, res) => { + res.should.have.status(200); + res.body.data.should.have.property("id"); + downloadId_esp32s2 = res.body.data.id; + done(); + }); + }); + + it("should compile the tof-distance-display sketch for senseBox MCU-S2 ESP32S2", (done) => { + const sketch = fs.readFileSync("test/sketches/mcu_s2/tof-distance-display.ino", "utf8"); + chai + .request(server) + .post("/compile") + .send({ board: "sensebox-esp32s2", sketch }) + .end((err, res) => { + res.should.have.status(200); + res.body.data.should.have.property("id"); + downloadId_esp32s2 = res.body.data.id; + done(); + }); + }); + + it("should download sketch for senseBox MCU-S2 ESP32S2", (done) => { + chai + .request(server) + .get("/download") + .query({ board: "sensebox-esp32s2", id: downloadId_esp32s2 }) + .end((err, res) => { + res.should.have.status(200); + res.header.should.have.property("content-disposition").eql("attachment; filename=sketch.bin"); + done(); + }); + }); +}); diff --git a/test/sketches/empty.ino b/test/sketches/empty.ino new file mode 100644 index 0000000..612c51b --- /dev/null +++ b/test/sketches/empty.ino @@ -0,0 +1,3 @@ +void setup() {} + +void loop() {} \ No newline at end of file diff --git a/test/sketches/hello-world.ino b/test/sketches/hello-world.ino new file mode 100644 index 0000000..53b6441 --- /dev/null +++ b/test/sketches/hello-world.ino @@ -0,0 +1,6 @@ +void setup() { + Serial.begin(9600); + Serial.println("Hello World"); +} + +void loop() {} \ No newline at end of file diff --git a/test/sketches/mcu/full-home.ino b/test/sketches/mcu/full-home.ino new file mode 100644 index 0000000..d1c6190 --- /dev/null +++ b/test/sketches/mcu/full-home.ino @@ -0,0 +1,885 @@ +/* + senseBox:home - Citizen Sensingplatform + Version: wifiv2_1.6.0 + Date: 2022-03-04 + Homepage: https://www.sensebox.de https://www.opensensemap.org + Author: Reedu GmbH & Co. KG + Note: Sketch for senseBox:home WiFi MCU Edition + Model: homeV2Wifi + Email: support@sensebox.de + Code is in the public domain. + https://github.com/sensebox/node-sketch-templater +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // http://librarymanager/All#Adafruit_DPS310 +#include + + +// Uncomment the next line to get debugging messages printed on the Serial port +// Do not leave this enabled for long time use +// #define ENABLE_DEBUG + +#ifdef ENABLE_DEBUG +#define DEBUG(str) Serial.println(str) +#define DEBUG_ARGS(str,str1) Serial.println(str,str1) +#define DEBUG2(str) Serial.print(str) +#define DEBUG_WRITE(c) Serial.write(c) +#else +#define DEBUG(str) +#define DEBUG_ARGS(str,str1) +#define DEBUG2(str) +#define DEBUG_WRITE(c) +#endif + +/* ------------------------------------------------------------------------- */ +/* ---------------------------------Metadata-------------------------------- */ +/* ------------------------------------------------------------------------- */ +/* SENSEBOX ID : 6734b900d48acc0007833483 */ +/* SENSEBOX NAME: compiler-home */ +/* ------------------------------------------------------------------------- */ +/* ------------------------------End of Metadata---------------------------- */ +/* ------------------------------------------------------------------------- */ + +/* ------------------------------------------------------------------------- */ +/* ------------------------------Configuration------------------------------ */ +/* ------------------------------------------------------------------------- */ + +// Wifi Credentials +const char *ssid = "manaus"; // your network SSID (name) +const char *pass = "piranha"; // your network password + +// Interval of measuring and submitting values in seconds +const unsigned int postingInterval = 60e3; + +// address of the server to send to +const char server[] PROGMEM = "ingress.opensensemap.org"; + +// senseBox ID +const char SENSEBOX_ID[] PROGMEM = "6734b900d48acc0007833483"; + +// Number of sensors +// Change this number if you add or remove sensors +// do not forget to remove or add the sensors on opensensemap.org +static const uint8_t NUM_SENSORS = 13; + +// Connected sensors +// Temperatur - HDC1080 +#define HDC1080_CONNECTED +// rel. Luftfeuchte - HDC1080 +#define HDC1080_CONNECTED +// Luftdruck - BMP280 +#define BMP280_CONNECTED +// Beleuchtungsstärke - TSL45315 +#define TSL45315_CONNECTED +// UV-Intensität - VEML6070 +#define VEML6070_CONNECTED +// Bodenfeuchte - SMT50 +#define SMT50_CONNECTED +// Bodentemperatur - SMT50 +#define SMT50_CONNECTED +// Lautstärke - SOUNDLEVELMETER +#define SOUNDLEVELMETER_CONNECTED +// CO2 - SCD30 +#define SCD30_CONNECTED +// PM1 - SPS30 +#define SPS30_CONNECTED +// PM2.5 - SPS30 +#define SPS30_CONNECTED +// PM4 - SPS30 +#define SPS30_CONNECTED +// PM10 - SPS30 +#define SPS30_CONNECTED + +// Display enabled +// Uncomment the next line to get values of measurements printed on display +#define DISPLAY128x64_CONNECTED + +// Sensor SENSOR_IDs +// Temperatur - HDC1080 +const char HDC1080_TEMPERSENSOR_ID[] PROGMEM = "6734b900d48acc0007833484"; +// rel. Luftfeuchte - HDC1080 +const char HDC1080_RELLUFSENSOR_ID[] PROGMEM = "6734b900d48acc0007833485"; +// Luftdruck - BMP280 +const char BMP280_LUFTDRSENSOR_ID[] PROGMEM = "6734b900d48acc0007833486"; +// Beleuchtungsstärke - TSL45315 +const char TSL45315_BELEUCSENSOR_ID[] PROGMEM = "6734b900d48acc0007833487"; +// UV-Intensität - VEML6070 +const char VEML6070_UVINTESENSOR_ID[] PROGMEM = "6734b900d48acc0007833488"; +// Bodenfeuchte - SMT50 +const char SMT50_BODENFSENSOR_ID[] PROGMEM = "6734b900d48acc0007833489"; +// Bodentemperatur - SMT50 +const char SMT50_BODENTSENSOR_ID[] PROGMEM = "6734b900d48acc000783348a"; +// Lautstärke - SOUNDLEVELMETER +const char SOUNDLEVELMETER_LAUTSTSENSOR_ID[] PROGMEM = "6734b900d48acc000783348b"; +// CO2 - SCD30 +const char SCD30_CO2SENSOR_ID[] PROGMEM = "6734b900d48acc000783348c"; +// PM1 - SPS30 +const char SPS30_PM1SENSOR_ID[] PROGMEM = "6734b900d48acc000783348d"; +// PM2.5 - SPS30 +const char SPS30_PM25SENSOR_ID[] PROGMEM = "6734b900d48acc000783348e"; +// PM4 - SPS30 +const char SPS30_PM4SENSOR_ID[] PROGMEM = "6734b900d48acc000783348f"; +// PM10 - SPS30 +const char SPS30_PM10SENSOR_ID[] PROGMEM = "6734b900d48acc0007833490"; + +WiFiClient wifiClient; +BearSSLClient client(wifiClient); + +unsigned long getTime() { + return WiFi.getTime(); +} + +//Load sensors / instances +#ifdef HDC1080_CONNECTED + Adafruit_HDC1000 HDC = Adafruit_HDC1000(); +#endif +#ifdef BMP280_CONNECTED + Adafruit_BMP280 BMP; +#endif +#ifdef TSL45315_CONNECTED + bool lightsensortype = 0; //0 for tsl - 1 for ltr + //settings for LTR sensor + LTR329 LTR; + unsigned char gain = 1; + unsigned char integrationTime = 0; + unsigned char measurementRate = 3; +#endif +#ifdef VEML6070_CONNECTED + VEML6070 VEML; +#endif +#ifdef SMT50_CONNECTED + #define SOILTEMPPIN 1 + #define SOILMOISPIN 2 +#endif +#ifdef SOUNDLEVELMETER_CONNECTED + #define SOUNDMETERPIN 3 +#endif +#ifdef BME680_CONNECTED + Adafruit_BME680 BME; +#endif +#ifdef WINDSPEED_CONNECTED + #define WINDSPEEDPIN 5 +#endif +#ifdef SCD30_CONNECTED + SCD30 SCD; +#endif +#ifdef DISPLAY128x64_CONNECTED +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define OLED_RESET 4 +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); +#endif +#ifdef DPS310_CONNECTED + Adafruit_DPS310 dps; +#endif +#ifdef SPS30_CONNECTED + uint32_t auto_clean_days = 4; + struct sps30_measurement m; + int16_t ret; + uint32_t auto_clean; +#endif + + +typedef struct measurement { + const char *sensorId; + float value; +} measurement; + +measurement measurements[NUM_SENSORS]; +uint8_t num_measurements = 0; + +// buffer for sprintf +char buffer[750]; + +/* ------------------------------------------------------------------------- */ +/* --------------------------End of Configuration--------------------------- */ +/* ------------------------------------------------------------------------- */ + +void addMeasurement(const char *sensorId, float value) { + measurements[num_measurements].sensorId = sensorId; + measurements[num_measurements].value = value; + num_measurements++; +} + +void writeMeasurementsToClient() { + // iterate throug the measurements array + for (uint8_t i = 0; i < num_measurements; i++) { + sprintf_P(buffer, PSTR("%s,%9.2f\n"), measurements[i].sensorId, + measurements[i].value); + + // transmit buffer to client + client.print(buffer); + DEBUG2(buffer); + } + + // reset num_measurements + num_measurements = 0; +} + +void submitValues() { + if (WiFi.status() != WL_CONNECTED) { + WiFi.disconnect(); + delay(1000); // wait 1s + WiFi.begin(ssid, pass); + delay(5000); // wait 5s + } + // close any connection before send a new request. + // This will free the socket on the WiFi shield + if (client.connected()) { + client.stop(); + delay(1000); + } + bool connected = false; + char _server[strlen_P(server)]; + strcpy_P(_server, server); + for (uint8_t timeout = 2; timeout != 0; timeout--) { + Serial.println(F("connecting...")); + connected = client.connect(_server, 443); + if (connected == true) { + DEBUG(F("Connection successful, transferring...")); + // construct the HTTP POST request: + sprintf_P(buffer, + PSTR("POST /boxes/%s/data HTTP/1.1\nAuthorization: 514975d906f5de2bc1581655c489700938c5c72bf2d16c2a7e72345324868ccb\nHost: %s\nContent-Type: " + "text/csv\nConnection: close\nContent-Length: %i\n\n"), + SENSEBOX_ID, server, num_measurements * 35); + DEBUG(buffer); + + // send the HTTP POST request: + client.print(buffer); + + // send measurements + writeMeasurementsToClient(); + + // send empty line to end the request + client.println(); + + uint16_t timeout = 0; + // allow the response to be computed + + while (timeout <= 5000) { + delay(10); + timeout = timeout + 10; + if (client.available()) { + break; + } + } + + while (client.available()) { + char c = client.read(); + DEBUG_WRITE(c); + // if the server's disconnected, stop the client: + if (!client.connected()) { + DEBUG(); + DEBUG(F("disconnecting from server.")); + client.stop(); + break; + } + } + + DEBUG(F("done!")); + + // reset number of measurements + num_measurements = 0; + break; + } + delay(1000); + } + + if (connected == false) { + // Reset durchführen + DEBUG(F("connection failed. Restarting System.")); + delay(5000); + noInterrupts(); + NVIC_SystemReset(); + while (1) + ; + } +} + +void checkI2CSensors() { + byte error; + int nDevices = 0; + byte sensorAddr[] = {41, 56, 57, 64, 97, 118}; + DEBUG(F("\nScanning...")); + for (int i = 0; i < sizeof(sensorAddr); i++) { + Wire.beginTransmission(sensorAddr[i]); + error = Wire.endTransmission(); + if (error == 0) { + nDevices++; + switch (sensorAddr[i]) + { + case 0x29: + DEBUG(F("TSL45315 found.")); + break; + case 0x38: // &0x39 + DEBUG(F("VEML6070 found.")); + break; + case 0x40: + DEBUG(F("HDC1080 found.")); + break; + case 0x76: + #ifdef BMP280_CONNECTED + DEBUG("BMP280 found."); + #elif defined(BME680_CONNECTED) + DEBUG("BME680 found."); + #else + DEBUG("DPS310 found."); + #endif + break; + case 0x61: + DEBUG("SCD30 found."); + break; + } + } + else if (error == 4) + { + DEBUG2(F("Unknown error at address 0x")); + if (sensorAddr[i] < 16) + DEBUG2(F("0")); + DEBUG_ARGS(sensorAddr[i], HEX); + } + } + if (nDevices == 0) { + DEBUG(F("No I2C devices found.\nCheck cable connections and press Reset.")); + while(true); + } else { + DEBUG2(nDevices); + DEBUG(F(" sensors found.\n")); + } +} + +void setup() { + // Initialize serial and wait for port to open: + #ifdef ENABLE_DEBUG + Serial.begin(9600); + #endif + delay(5000); + + DEBUG2(F("xbee1 spi enable...")); + senseBoxIO.SPIselectXB1(); // select XBEE1 spi + DEBUG(F("done")); + senseBoxIO.powerXB1(false); + delay(200); + DEBUG2(F("xbee1 power on...")); + senseBoxIO.powerXB1(true); // power ON XBEE1 + DEBUG(F("done")); + senseBoxIO.powerI2C(false); + delay(200); + senseBoxIO.powerI2C(true); + delay(200); + senseBoxIO.powerUART(true); + +#ifdef DISPLAY128x64_CONNECTED + DEBUG2(F("enable display...")); + delay(2000); + display.begin(SSD1306_SWITCHCAPVCC, 0x3D); + display.display(); + delay(100); + display.clearDisplay(); + DEBUG(F("done.")); + display.setCursor(0, 0); + display.setTextSize(2); + display.setTextColor(WHITE, BLACK); + display.println("senseBox:"); + display.println("home\n"); + display.setTextSize(1); + display.println("Version Wifi "); + display.setTextSize(2); + display.display(); + delay(2000); + display.clearDisplay(); + display.setCursor(0, 0); + display.setTextSize(1); + display.println("Connecting to:"); + display.println(); + display.println(ssid); + display.setTextSize(1); + display.display(); +#endif + + // Check WiFi Bee status + if (WiFi.status() == WL_NO_SHIELD) { + DEBUG(F("WiFi shield not present")); + // don't continue: + while (true); + } + uint8_t status = WL_IDLE_STATUS; + // attempt to connect to Wifi network: + while (status != WL_CONNECTED) { +#ifdef DISPLAY128x64_CONNECTED + display.print("."); + display.display(); +#endif + DEBUG2(F("Attempting to connect to SSID: ")); + DEBUG(ssid); + // Connect to WPA/WPA2 network. Change this line if using open or WEP + // network + status = WiFi.begin(ssid, pass); + // wait 10 seconds for connection: + DEBUG2(F("Waiting 10 seconds for connection...")); + delay(10000); + DEBUG(F("done.")); + } + + // check the server time for the validation of the certificate + ArduinoBearSSL.onGetTime(getTime); + + #ifdef ENABLE_DEBUG + // init I2C/wire library + Wire.begin(); + checkI2CSensors(); + #endif + + // Sensor initialization + DEBUG(F("Initializing sensors...")); + #ifdef HDC1080_CONNECTED + HDC.begin(); + #endif + #ifdef BMP280_CONNECTED + BMP.begin(0x76); + #endif + #ifdef VEML6070_CONNECTED + VEML.begin(); + delay(500); + #endif + #ifdef TSL45315_CONNECTED + Lightsensor_begin(); + #endif + #ifdef BME680_CONNECTED + BME.begin(0x76); + BME.setTemperatureOversampling(BME680_OS_8X); + BME.setHumidityOversampling(BME680_OS_2X); + BME.setPressureOversampling(BME680_OS_4X); + BME.setIIRFilterSize(BME680_FILTER_SIZE_3); + #endif + #ifdef SCD30_CONNECTED + Wire.begin(); + SCD.begin(); + #endif + #ifdef DISPLAY128x64_CONNECTED + display.clearDisplay(); + display.setCursor(30, 28); + display.setTextSize(2); + display.print("Ready!"); + display.display(); + #endif + #ifdef DPS310_CONNECTED + dps.begin_I2C(0x76); + dps.configurePressure(DPS310_64HZ, DPS310_64SAMPLES); + dps.configureTemperature(DPS310_64HZ, DPS310_64SAMPLES); + #endif + #ifdef SPS30_CONNECTED + sensirion_i2c_init(); + ret = sps30_set_fan_auto_cleaning_interval_days(auto_clean_days); + ret = sps30_start_measurement(); + #endif + DEBUG(F("Initializing sensors done!")); + DEBUG(F("Starting loop in 3 seconds.")); + delay(3000); +} + + +void loop() { + DEBUG(F("Starting new measurement...")); +#ifdef DISPLAY128x64_CONNECTED + long displayTime = 5000; + int page = 0; + + display.clearDisplay(); + display.setCursor(0, 0); + display.setTextSize(1); + display.setTextColor(WHITE, BLACK); + display.println("Uploading new measurement... "); + display.display(); +#endif + // capture loop start timestamp + unsigned long start = millis(); + + //-----Temperature-----// + //-----Humidity-----// +#ifdef HDC1080_CONNECTED + addMeasurement(HDC1080_TEMPERSENSOR_ID, HDC.readTemperature()); + delay(200); + addMeasurement(HDC1080_RELLUFSENSOR_ID, HDC.readHumidity()); +#endif + + //-----Pressure-----// +#ifdef BMP280_CONNECTED + float pressure; + pressure = BMP.readPressure() / 100; + addMeasurement(BMP280_LUFTDRSENSOR_ID, pressure); +#endif + + //-----Lux-----// +#ifdef TSL45315_CONNECTED + addMeasurement(TSL45315_BELEUCSENSOR_ID, Lightsensor_getIlluminance()); +#endif + + //-----UV intensity-----// +#ifdef VEML6070_CONNECTED + addMeasurement(VEML6070_UVINTESENSOR_ID, VEML.getUV()); +#endif + + //-----Soil Temperature & Moisture-----// +#ifdef SMT50_CONNECTED + float voltage = analogRead(SOILTEMPPIN) * (3.3 / 1024.0); + float soilTemperature = (voltage - 0.5) * 100; + addMeasurement(SMT50_BODENTSENSOR_ID, soilTemperature); + voltage = analogRead(SOILMOISPIN) * (3.3 / 1024.0); + float soilMoisture = (voltage * 50) / 3; + addMeasurement(SMT50_BODENFSENSOR_ID, soilMoisture); +#endif + + //-----dB(A) Sound Level-----// +#ifdef SOUNDLEVELMETER_CONNECTED + float v = analogRead(SOUNDMETERPIN) * (3.3 / 1024.0); + float decibel = v * 50; + addMeasurement(SOUNDLEVELMETER_LAUTSTSENSOR_ID, decibel); +#endif + + //-----BME680-----// +#ifdef BME680_CONNECTED + BME.setGasHeater(0, 0); + float gasResistance; + if ( BME.performReading()) { + addMeasurement(BME680_LUFTTESENSOR_ID, BME.temperature - 1); + addMeasurement(BME680_LUFTFESENSOR_ID, BME.humidity); + addMeasurement(BME680_ATMLUFSENSOR_ID, BME.pressure / 100); + } + BME.setGasHeater(320, 150); // 320*C for 150 ms + if ( BME.performReading()) { + gasResistance = BME.gas_resistance / 1000.0; + addMeasurement(BME680_VOCSENSOR_ID, gasResistance); + } +#endif + + //-----Wind speed-----// +#ifdef WINDSPEED_CONNECTED + float voltageWind = analogRead(WINDSPEEDPIN) * (3.3 / 1024.0); + float windspeed = 0.0; + if (voltageWind >= 0.018) { + float poly1 = pow(voltageWind, 3); + poly1 = 17.0359801998299 * poly1; + float poly2 = pow(voltageWind, 2); + poly2 = 47.9908168343362 * poly2; + float poly3 = 122.899677524413 * voltageWind; + float poly4 = 0.657504127272728; + windspeed = poly1 - poly2 + poly3 - poly4; + windspeed = windspeed * 0.2777777777777778; //conversion in m/s + } + addMeasurement(WINDSPEED_WINDGESENSOR_ID, windspeed); +#endif + + //-----CO2-----// +#ifdef SCD30_CONNECTED + addMeasurement(SCD30_CO2SENSOR_ID, SCD.getCO2()); +#endif + + //-----DPS310 Pressure-----// + #ifdef DPS310_CONNECTED + sensors_event_t temp_event, pressure_event; + dps.getEvents(&temp_event, &pressure_event); + addMeasurement(DPS310_LUFTDRSENSOR_ID, pressure_event.pressure); + #endif + + #ifdef SPS30_CONNECTED + ret = sps30_read_measurement(&m); + addMeasurement(SPS30_PM1SENSOR_ID, m.mc_1p0); + addMeasurement(SPS30_PM25SENSOR_ID, m.mc_2p5); + addMeasurement(SPS30_PM4SENSOR_ID, m.mc_4p0); + addMeasurement(SPS30_PM10SENSOR_ID, m.mc_10p0); + #endif + + DEBUG(F("Submit values")); + submitValues(); + + // schedule next round of measurements + for (;;) { + unsigned long now = millis(); + unsigned long elapsed = now - start; +#ifdef DISPLAY128x64_CONNECTED + display.clearDisplay(); + display.setCursor(0, 0); + display.setTextSize(1); + display.setTextColor(WHITE, BLACK); + switch (page) + { + case 0: + // HDC & BMP + display.setTextSize(2); + display.setTextColor(BLACK, WHITE); + display.println(F("HDC&BMP")); + display.setTextColor(WHITE, BLACK); + display.setTextSize(1); + display.print(F("Temp:")); +#ifdef HDC1080_CONNECTED + display.println(HDC.readTemperature()); +#else + display.println(F("not connected")); +#endif + display.println(); + display.print(F("Humi:")); +#ifdef HDC1080_CONNECTED + display.println(HDC.readHumidity()); +#else + display.println(F("not connected")); +#endif + display.println(); + display.print(F("Press.:")); +#ifdef BMP280_CONNECTED + display.println(BMP.readPressure() / 100); +#else + display.println(F("not connected")); +#endif + break; + case 1: + // TSL/VEML + display.setTextSize(2); + display.setTextColor(BLACK, WHITE); + display.println(F("TSL&VEML")); + display.setTextColor(WHITE, BLACK); + display.println(); + display.setTextSize(1); + display.print(F("Lux:")); +#ifdef TSL45315_CONNECTED + display.println(Lightsensor_getIlluminance()); +#else + display.println(F("not connected")); +#endif + display.println(); + display.print("UV:"); +#ifdef VEML6070_CONNECTED + display.println(VEML.getUV()); +#else + display.println(F("not connected")); +#endif + break; + case 2: + // SPS30_CONNECTED + display.setTextSize(2); + display.setTextColor(BLACK, WHITE); + display.println(F("PM1&PM2.5")); + display.setTextColor(WHITE, BLACK); + display.println(); + display.setTextSize(1); + display.print(F("PM1:")); + + #ifdef SPS30_CONNECTED + display.println(m.mc_1p0); + #else + display.println(F("not connected")); + #endif + + display.print(F("PM.25:")); + #ifdef SPS30_CONNECTED + display.println(m.mc_2p5); + #else + display.println(F("not connected")); + #endif + + break; + case 3: + // SPS30_CONNECTED + display.setTextSize(2); + display.setTextColor(BLACK, WHITE); + display.println(F("PM4&PM10")); + display.setTextColor(WHITE, BLACK); + display.println(); + display.setTextSize(1); + display.print(F("PM4:")); + + #ifdef SPS30_CONNECTED + display.println(m.mc_4p0); + #else + display.println(F("not connected")); + #endif + display.print(F("PM10:")); + #ifdef SPS30_CONNECTED + display.println(m.mc_10p0); + #else + display.println(F("not connected")); + #endif + break; + case 4: + // SMT, SOUND LEVEL , BME + display.setTextSize(2); + display.setTextColor(BLACK, WHITE); + display.println(F("Soil")); + display.setTextColor(WHITE, BLACK); + display.println(); + display.setTextSize(1); + display.print(F("Temp:")); +#ifdef SMT50_CONNECTED + display.println(soilTemperature); +#else + display.println(F("not connected")); +#endif + display.println(); + display.print(F("Moist:")); +#ifdef SMT50_CONNECTED + display.println(soilMoisture); +#else + display.println(F("not connected")); +#endif + + break; + case 5: + // WINDSPEED SCD30 + display.setTextSize(2); + display.setTextColor(BLACK, WHITE); + display.println(F("Wind&SCD30")); + display.setTextColor(WHITE, BLACK); + display.println(); + display.setTextSize(1); + display.print(F("Speed:")); +#ifdef WINDSPEED_CONNECTED + display.println(windspeed); +#else + display.println(F("not connected")); +#endif + display.println(); + display.print(F("SCD30:")); +#ifdef SCD30_CONNECTED + display.println(SCD.getCO2()); +#else + display.println(F("not connected")); +#endif + break; + case 6: + // SMT, SOUND LEVEL , BME + display.setTextSize(2); + display.setTextColor(BLACK, WHITE); + display.println(F("Sound&BME")); + display.setTextColor(WHITE, BLACK); + display.println(); + display.setTextSize(1); + display.print(F("Sound:")); +#ifdef SOUNDLEVELMETER_CONNECTED + display.println(decibel); +#else + display.println(F("not connected")); +#endif + display.println(); + display.print(F("Gas:")); +#ifdef BME680_CONNECTED + display.println(gasResistance); +#else + display.print(F("not connected")); +#endif + + break; + } + display.display(); + if (elapsed >= displayTime) + { + if (page == 4) + { + page = 0; + } + else + { + page += 1; + } + displayTime += 5000; + } +#endif + if (elapsed >= postingInterval) + return; + } +} + +int read_reg(byte address, uint8_t reg) +{ + int i = 0; + + Wire.beginTransmission(address); + Wire.write(reg); + Wire.endTransmission(); + Wire.requestFrom((uint8_t)address, (uint8_t)1); + delay(1); + if(Wire.available()) + i = Wire.read(); + + return i; +} + +void write_reg(byte address, uint8_t reg, uint8_t val) +{ + Wire.beginTransmission(address); + Wire.write(reg); + Wire.write(val); + Wire.endTransmission(); +} + +#ifdef TSL45315_CONNECTED +void Lightsensor_begin() +{ + Wire.begin(); + unsigned int u = 0; + DEBUG(F("Checking lightsensortype")); + u = read_reg(0x29, 0x80 | 0x0A); //id register + if ((u & 0xF0) == 0xA0) // TSL45315 + { + DEBUG(F("TSL45315")); + write_reg(0x29, 0x80 | 0x00, 0x03); //control: power on + write_reg(0x29, 0x80 | 0x01, 0x02); //config: M=4 T=100ms + delay(120); + lightsensortype = 0; //TSL45315 + } + else + { + DEBUG(F("LTR329")); + LTR.begin(); + LTR.setControl(gain, false, false); + LTR.setMeasurementRate(integrationTime, measurementRate); + LTR.setPowerUp(); //power on with default settings + delay(10); //Wait 10 ms (max) - wakeup time from standby + lightsensortype = 1; // + } +} + +unsigned int Lightsensor_getIlluminance() +{ + unsigned int lux = 0; + if (lightsensortype == 0) // TSL45315 + { + unsigned int u = (read_reg(0x29, 0x80 | 0x04) << 0); //data low + u |= (read_reg(0x29, 0x80 | 0x05) << 8); //data high + lux = u * 4; // calc lux with M=4 and T=100ms + } + else if (lightsensortype == 1) //LTR-329ALS-01 + { + delay(100); + unsigned int data0, data1; + for (int i = 0; i < 5; i++) { + if (LTR.getData(data0, data1)) { + if(LTR.getLux(gain, integrationTime, data0, data1, lux)); + else DEBUG(F("LTR sensor saturated")); + if(lux > 0) break; + else delay(10); + } + else { + DEBUG2(F("LTR getData error ")); + byte error = LTR.getError(); + Serial.println(error); + } + } + } + return lux; +} +#endif diff --git a/test/sketches/mcu_s2/tof-distance-display.ino b/test/sketches/mcu_s2/tof-distance-display.ino new file mode 100644 index 0000000..ec6887f --- /dev/null +++ b/test/sketches/mcu_s2/tof-distance-display.ino @@ -0,0 +1,64 @@ +// Code generated by senseBox Blockly on Wed Nov 13 2024 14:34:47 GMT+0100 (Central European Standard Time) + +#include +#include +#include // http://librarymanager/All#Adafruit_GFX_Library +#include // http://librarymanager/All#Adafruit_SSD1306 +#include + +VL53L8CX sensor_vl53l8cx(&Wire, -1, -1); + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define OLED_RESET -1 +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); + +float oldVl53l8cxMin = -1.0; +float getVl53l8cxMin() { + VL53L8CX_ResultsData Results; + uint8_t NewDataReady = 0; + uint8_t status; + + status = sensor_vl53l8cx.check_data_ready(&NewDataReady); + + if ((!status) && (NewDataReady != 0)) { + sensor_vl53l8cx.get_ranging_data(&Results); + float min = 10000.0; + for(int i = 0; i < VL53L8CX_RESOLUTION_8X8*VL53L8CX_NB_TARGET_PER_ZONE; i++) { + if((&Results)->target_status[i]!=255){ + float distance = ((&Results)->distance_mm[i])/10; + if(min > distance) { + min = distance; + } + } + } + oldVl53l8cxMin = (min==10000.0) ? 0.0 : min; + } + return oldVl53l8cxMin; +} + +void setup() { + display.begin(SSD1306_SWITCHCAPVCC, 0x3D); + display.display(); + delay(100); + display.clearDisplay(); + + Wire.begin(); + Wire.setClock(1000000); //Sensor has max I2C freq of 1MHz + sensor_vl53l8cx.begin(); + sensor_vl53l8cx.init(); + sensor_vl53l8cx.set_ranging_frequency_hz(30); + sensor_vl53l8cx.set_resolution(VL53L8CX_RESOLUTION_8X8); + sensor_vl53l8cx.start_ranging(); + +} + +void loop() { + display.clearDisplay(); + display.setCursor(0,0); + display.setTextSize(1); + display.setTextColor(WHITE,BLACK); + display.println(getVl53l8cxMin()); + display.display(); + +} \ No newline at end of file diff --git a/test/test.js b/test/test.js deleted file mode 100644 index ccd84f7..0000000 --- a/test/test.js +++ /dev/null @@ -1,313 +0,0 @@ -process.env.NODE_ENV = "test"; - -const chai = require("chai"); -const chaiHttp = require("chai-http"); -const server = require("../src/index"); -const should = chai.should(); - -chai.use(chaiHttp); - -const params = { - board: "sensebox-mcu", - sketch: - 'void setup() {\nSerial.begin(9600);\nSerial.println("Hello World");\n}\nvoid loop() {}', -}; - -let downloadId_mcu = ""; -let downloadId_uno = ""; -let downloadId_esp32s2 = ""; - -describe("Compiler", () => { - describe("/GET index", () => { - it("it should get the index page and answer with a 404 ", (done) => { - chai - .request(server) - .get("/") - .end((err, res) => { - res.should.have.status(404); - done(); - }); - }); - }); - - describe("/compile", () => { - it("should compile a sketch for senseBox MCU", (done) => { - chai - .request(server) - .post("/compile") - .send(params) - .end((err, res) => { - res.should.have.status(200); - res.body.should.be.a("object"); - res.body.should.have - .property("message") - .eql("Sketch successfully compiled and created!"); - res.body.should.have.property("data"); - res.body.data.should.be.a("object"); - res.body.data.should.have.property("id"); - downloadId_mcu = res.body.data.id; - done(); - }); - }); - - it("should compile a sketch for old senseBox", (done) => { - const { sketch } = params; - chai - .request(server) - .post("/compile") - .send({ - board: "sensebox", - sketch, - }) - .end((err, res) => { - res.should.have.status(200); - res.body.should.be.a("object"); - res.body.should.have - .property("message") - .eql("Sketch successfully compiled and created!"); - res.body.should.have.property("data"); - res.body.data.should.be.a("object"); - res.body.data.should.have.property("id"); - downloadId_uno = res.body.data.id; - done(); - }); - }); - - it("should compile a sketch for senseBox MCU-S2 ESP32S2", (done) => { - const { sketch } = params; - chai - .request(server) - .post("/compile") - .send({ - board: "sensebox-esp32s2", - sketch, - }) - .end((err, res) => { - res.should.have.status(200); - res.body.should.be.a("object"); - res.body.should.have - .property("message") - .eql("Sketch successfully compiled and created!"); - res.body.should.have.property("data"); - res.body.data.should.be.a("object"); - res.body.data.should.have.property("id"); - downloadId_esp32s2 = res.body.data.id; - done(); - }); - }); - - it("should reject request without board parameter", (done) => { - const { sketch } = params; - chai - .request(server) - .post("/compile") - .send({ sketch }) - .end((err, res) => { - res.should.have.status(422); - res.body.should.be.a("object"); - res.body.should.have - .property("message") - .eql("Parameters 'sketch' and 'board' are required"); - done(); - }); - }); - - it("should reject request without sketch parameter", (done) => { - const { board } = params; - chai - .request(server) - .post("/compile") - .send({ board }) - .end((err, res) => { - res.should.have.status(422); - res.body.should.be.a("object"); - res.body.should.have - .property("message") - .eql("Parameters 'sketch' and 'board' are required"); - done(); - }); - }); - - it("should reject request with invalid board", (done) => { - const { sketch } = params; - chai - .request(server) - .post("/compile") - .send({ - board: "esp8266", - sketch, - }) - .end((err, res) => { - res.should.have.status(422); - res.body.should.be.a("object"); - res.body.should.have - .property("message") - .eql( - "Invalid board parameter. Valid values are: sensebox-mcu,sensebox,sensebox-esp32s2" - ); - done(); - }); - }); - - it("should reject request with wrong Content-Type", (done) => { - chai - .request(server) - .post("/compile") - .set("Content-Type", "text/plain") - .send("") - .end((err, res) => { - res.should.have.status(415); - res.body.should.be.a("object"); - res.body.should.have - .property("message") - .eql( - "Invalid Content-Type. Only application/json Content-Type allowed." - ); - done(); - }); - }); - - it("should only accept POST request", (done) => { - chai - .request(server) - .get("/compile") - .send(params) - .end((err, res) => { - res.should.have.status(405); - res.body.should.be.a("object"); - res.body.should.have - .property("message") - .eql( - "Invalid HTTP method. Only POST requests allowed on /compile." - ); - done(); - }); - }); - }); - - describe("/download", () => { - it("should only accept /GET requests", (done) => { - chai - .request(server) - .post("/download") - .send({}) - .end((err, res) => { - res.should.have.status(405); - res.body.should.be.a("object"); - res.body.should.have - .property("message") - .eql( - "Invalid HTTP method. Only GET requests allowed on /download." - ); - done(); - }); - }); - - it("should download sketch for senseBox MCU", (done) => { - chai - .request(server) - .get("/download") - .query({ board: "sensebox-mcu", id: downloadId_mcu }) - .end((err, res) => { - res.should.have.status(200); - res.header.should.be.a("object"); - res.header.should.have - .property("content-disposition") - .eql("attachment; filename=sketch.bin"); - done(); - }); - }); - - it("should download sketch for old senseBox", (done) => { - chai - .request(server) - .get("/download") - .query({ board: "sensebox", id: downloadId_uno }) - .end((err, res) => { - res.should.have.status(200); - res.header.should.be.a("object"); - res.header.should.have - .property("content-disposition") - .eql("attachment; filename=sketch.hex"); - done(); - }); - }); - - it("should download sketch for senseBox MCU-S2 ESP32S2", (done) => { - chai - .request(server) - .get("/download") - .query({ board: "sensebox-esp32s2", id: downloadId_esp32s2 }) - .end((err, res) => { - res.should.have.status(200); - res.header.should.be.a("object"); - res.header.should.have - .property("content-disposition") - .eql("attachment; filename=sketch.bin"); - done(); - }); - }); - - it("should reject request without id parameter", (done) => { - chai - .request(server) - .get("/download") - .send({ - board: "sensebox-mcu", - }) - .end((err, res) => { - res.should.have.status(422); - res.body.should.be.a("object"); - res.body.should.have - .property("message") - .eql("Parameters 'id' and 'board' are required"); - done(); - }); - }); - - it("should reject request without board parameter", (done) => { - chai - .request(server) - .get("/download") - .send({ - id: downloadId_mcu, - }) - .end((err, res) => { - res.should.have.status(422); - res.body.should.be.a("object"); - res.body.should.have - .property("message") - .eql("Parameters 'id' and 'board' are required"); - done(); - }); - }); - - it("should set a custom filename", (done) => { - chai - .request(server) - .post("/compile") - .send(params) - .then((res) => { - return res.body.data.id; - }) - .then((downloadId) => { - chai - .request(server) - .get("/download") - .query({ - board: "sensebox-mcu", - id: downloadId, - filename: "custom", - }) - .end((err, res) => { - res.should.have.status(200); - res.header.should.be.a("object"); - res.header.should.have - .property("content-disposition") - .eql("attachment; filename=custom.bin"); - done(); - }); - }); - }); - }); -}); diff --git a/test/uno.js b/test/uno.js new file mode 100644 index 0000000..4ef633c --- /dev/null +++ b/test/uno.js @@ -0,0 +1,65 @@ +const chai = require("chai"); +const chaiHttp = require("chai-http"); +const server = require("../src/index"); +const should = chai.should(); +const fs = require("fs"); + +chai.use(chaiHttp); + +describe("Compiler - UNO", () => { + let downloadId_uno = ""; + + it("should compile an empty sketch for old senseBox", (done) => { + const sketch = fs.readFileSync("test/sketches/empty.ino", "utf8"); + + chai + .request(server) + .post("/compile") + .send({ board: "sensebox", sketch }) + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.a("object"); + res.body.should.have + .property("message") + .eql("Sketch successfully compiled and created!"); + res.body.should.have.property("data"); + res.body.data.should.be.a("object"); + res.body.data.should.have.property("id"); + downloadId_uno = res.body.data.id; + done(); + }); + }); + + it("should compile a hello world sketch for old senseBox", (done) => { + const sketch = fs.readFileSync("test/sketches/hello-world.ino", "utf8"); + + chai + .request(server) + .post("/compile") + .send({ board: "sensebox", sketch }) + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.a("object"); + res.body.should.have + .property("message") + .eql("Sketch successfully compiled and created!"); + res.body.should.have.property("data"); + res.body.data.should.be.a("object"); + res.body.data.should.have.property("id"); + downloadId_uno = res.body.data.id; + done(); + }); + }); + + it("should download sketch for old senseBox", (done) => { + chai + .request(server) + .get("/download") + .query({ board: "sensebox", id: downloadId_uno }) + .end((err, res) => { + res.should.have.status(200); + res.header.should.have.property("content-disposition").eql("attachment; filename=sketch.hex"); + done(); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index f25b099..cf04861 100644 --- a/yarn.lock +++ b/yarn.lock @@ -196,6 +196,13 @@ debug@^3.1.0: dependencies: ms "^2.1.1" +debug@^4.1.1: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + deep-eql@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" @@ -398,6 +405,11 @@ isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +lodash@^4.17.15: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + loupe@^2.3.1: version "2.3.4" resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.4.tgz#7e0b9bffc76f148f9be769cb1321d3dcf3cb25f3" @@ -458,6 +470,19 @@ mkdirp@0.5.1: dependencies: minimist "0.0.8" +mocha-ctrf-json-reporter@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/mocha-ctrf-json-reporter/-/mocha-ctrf-json-reporter-0.0.6.tgz#b27dfd01570a28f90f41d2cc12f8228142e6ec85" + integrity sha512-a5wBPTIYDfbSHKKYH6iM6Ut1PT2gpOoGe4tXB3SFYSou5QGUEcWHKBcHpnWGxVRUcYDMtNPU6n1127OHgS3qCA== + +mocha-multi-reporters@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/mocha-multi-reporters/-/mocha-multi-reporters-1.5.1.tgz#c73486bed5519e1d59c9ce39ac7a9792600e5676" + integrity sha512-Yb4QJOaGLIcmB0VY7Wif5AjvLMUFAdV57D2TWEva1Y0kU/3LjKpeRVmlMIfuO1SVbauve459kgtIizADqxMWPg== + dependencies: + debug "^4.1.1" + lodash "^4.17.15" + mocha@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" @@ -491,7 +516,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@^2.1.1: +ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==