diff --git a/Dockerfile b/Dockerfile index 516b006..4e7e1f4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,12 @@ FROM node:17 -ENV ARDUINO_CLI_VERSION=0.29.0 \ +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.9 \ SENSEBOXCORE_URL=https://raw.githubusercontent.com/mariopesch/senseBoxMCU-core/master/package_sensebox_index.json \ + ESP32CORE_URL=https://mariopesch.github.io/sensebox-esp32-dev/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 \ @@ -13,7 +15,8 @@ ENV ARDUINO_CLI_VERSION=0.29.0 \ 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 + AMS5915_LIBRARY_URL=https://github.com/bolderflight/ams5915/archive/refs/heads/main.zip \ + PHYPHOX_BETA_URL=https://github.com/sensebox/phyphox-arduino/archive/refs/tags/v1.2.2-beta.zip RUN apt-get update && apt-get install -y xz-utils unzip wget @@ -35,6 +38,13 @@ RUN arduino-cli core install arduino:samd@${ARDUINO_SAMD_VERSION} 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 + + 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 \ @@ -52,7 +62,9 @@ RUN wget -O ssd1306_plot_library.zip $SSD1306_PLOT_LIBRARY_URL \ && 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 + && arduino-cli lib install --zip-path ams5915_library.zip \ + && wget -O phyphox_beta_library.zip $PHYPHOX_BETA_URL \ + && arduino-cli lib install --zip-path phyphox_beta_library.zip # install Libraries with arduino-cli RUN arduino-cli lib install "Ethernet" @@ -68,6 +80,7 @@ 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" @@ -84,14 +97,19 @@ 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 "phyphox BLE" //remove until final release 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" +RUN arduino-cli lib install "STM32duino VL53L8CX" +RUN arduino-cli lib install "Adafruit ICM20X" +RUN arduino-cli lib install "NeoGPS" +RUN arduino-cli lib install "Adafruit NeoMatrix" WORKDIR /app diff --git a/Dockerfile.test b/Dockerfile.test index 59fe216..15aa2c7 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -1,10 +1,12 @@ FROM node:17 -ENV ARDUINO_CLI_VERSION=0.21.1 \ +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.9 \ SENSEBOXCORE_URL=https://raw.githubusercontent.com/mariopesch/senseBoxMCU-core/master/package_sensebox_index.json \ + ESP32CORE_URL=https://mariopesch.github.io/sensebox-esp32-dev/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 \ @@ -13,7 +15,7 @@ ENV ARDUINO_CLI_VERSION=0.21.1 \ 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 \ - TINYGPS_LIBRARY_URL=https://github.com/mikalhart/TinyGPSPlus/archive/refs/tags/v1.0.2b.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 @@ -35,6 +37,13 @@ RUN arduino-cli core install arduino:samd@${ARDUINO_SAMD_VERSION} 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 + + 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 \ @@ -50,9 +59,9 @@ RUN wget -O ssd1306_plot_library.zip $SSD1306_PLOT_LIBRARY_URL \ && 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 tinygps_library.zip $TINYGPS_LIBRARY_URL \ - && arduino-cli lib install --zip-path tinygps_library.zip + && 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" @@ -68,6 +77,7 @@ 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" @@ -86,14 +96,21 @@ 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 - +RUN yarn COPY src /app/src COPY test /app/test diff --git a/README.md b/README.md index d58955f..cdccd81 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ You can also run the container image mutliple times. See [Scaling with docker-co - have `application/json` as `content-type` - contain a valid JSON string with keys `board` and `sketch` with non-empty values. -Possible `board` values are `sensebox-mcu` for the new senseBox MCU and `sensebox` for the old Arduino Uno based senseBox. +Possible `board` values are `sensebox-mcu` for the new senseBox MCU, `sensebox` for the old Arduino Uno based senseBox and `sensebox-esp32s2` for the esp32s2 based MCU. The `sketch` value should be a valid Arduino sketch. diff --git a/src/builder.js b/src/builder.js index f1254b5..e791c3f 100644 --- a/src/builder.js +++ b/src/builder.js @@ -6,14 +6,16 @@ const fs = require("fs"); const boardFQBNs = { "sensebox-mcu": "sensebox:samd:sb:power=on", - sensebox: "arduino:avr:uno", + "sensebox": "arduino:avr:uno", + "sensebox-esp32s2": "esp32:esp32:sensebox_mcu_esp32s2", }; const validBoards = Object.keys(boardFQBNs); const boardBinaryFileextensions = { "sensebox-mcu": "bin", - sensebox: "hex", + "sensebox": "hex", + "sensebox-esp32s2": "bin", }; const baseArgs = ["--build-cache-path", `/app/src/build-cache`]; diff --git a/test/test.js b/test/test.js index 9c34ca1..ccd84f7 100644 --- a/test/test.js +++ b/test/test.js @@ -1,220 +1,313 @@ -process.env.NODE_ENV = 'test'; +process.env.NODE_ENV = "test"; -const chai = require('chai'); -const chaiHttp = require('chai-http'); -const server = require('../src/index'); +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() {}' + board: "sensebox-mcu", + sketch: + 'void setup() {\nSerial.begin(9600);\nSerial.println("Hello World");\n}\nvoid loop() {}', }; -let downloadId_mcu = ''; -let downloadId_uno = ''; +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('/') +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') + 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'); + 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) => { + it("should compile a sketch for old senseBox", (done) => { const { sketch } = params; - chai.request(server) - .post('/compile') + chai + .request(server) + .post("/compile") .send({ - board: 'sensebox', - sketch + 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'); + 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) => { + it("should reject request without board parameter", (done) => { const { sketch } = params; - chai.request(server) - .post('/compile') + 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"); + 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) => { + it("should reject request without sketch parameter", (done) => { const { board } = params; - chai.request(server) - .post('/compile') + 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"); + 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) => { + it("should reject request with invalid board", (done) => { const { sketch } = params; - chai.request(server) - .post('/compile') + chai + .request(server) + .post("/compile") .send({ - board: 'esp8266', - sketch + 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"); + 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('') + 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."); + 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') + 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."); + 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') + 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."); + 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 senseBox MCU', (done) => { - chai.request(server) - .get('/download') - .query({ board: 'sensebox-mcu', id: downloadId_mcu }) + 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.bin"); + res.header.should.be.a("object"); + res.header.should.have + .property("content-disposition") + .eql("attachment; filename=sketch.hex"); done(); }); }); - it('should download sketch for old senseBox', (done) => { - chai.request(server) - .get('/download') - .query({ board: 'sensebox', id: downloadId_uno }) + 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.hex"); + 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') + it("should reject request without id parameter", (done) => { + chai + .request(server) + .get("/download") .send({ - board: 'sensebox-mcu' + 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"); + 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') + it("should reject request without board parameter", (done) => { + chai + .request(server) + .get("/download") .send({ - id: downloadId_mcu + 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"); + 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') + 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' }) + 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"); + res.header.should.be.a("object"); + res.header.should.have + .property("content-disposition") + .eql("attachment; filename=custom.bin"); done(); }); - }) + }); }); }); -}); \ No newline at end of file +});