diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index e526286..45efc0f 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -26,19 +26,14 @@ env: jobs: compile-test: - name: compile for ${{ matrix.fqbn }} + name: ${{ matrix.board.fqbn }} runs-on: ubuntu-latest env: # libraries to install for all boards UNIVERSAL_LIBRARIES: | - # Install the ArduinoThreads library from the repository + # Install the Arduino_Threads library from the repository - source-path: ./ - - name: Arduino_LSM9DS1 - - name: Arduino_APDS9960 - - name: ArduinoECCX08 - - name: Arduino_HTS221 - - name: OneWire # sketch paths to compile (recursive) for all boards UNIVERSAL_SKETCH_PATHS: | - examples @@ -49,10 +44,19 @@ jobs: fail-fast: false matrix: - fqbn: - - "arduino:mbed:nano33ble" - # - "arduino:mbed:envie_m4" - - "arduino:mbed:envie_m7" + board: + - fqbn: arduino:mbed_nano:nano33ble + platforms: | + - name: arduino:mbed_nano + - fqbn: arduino:mbed_nano:nanorp2040connect + platforms: | + - name: arduino:mbed_nano + - fqbn: arduino:mbed_portenta:envie_m4 + platforms: | + - name: arduino:mbed_portenta + - fqbn: arduino:mbed_portenta:envie_m7 + platforms: | + - name: arduino:mbed_portenta steps: - name: Checkout @@ -83,15 +87,10 @@ jobs: uses: arduino/compile-sketches@v1 with: cli-version: 'arduino_threads' - fqbn: ${{ matrix.fqbn }} + fqbn: ${{ matrix.board.fqbn }} libraries: | ${{ env.UNIVERSAL_LIBRARIES }} - platforms: | - # Use Board Manager to install the latest release of Arduino mbed Boards to get the toolchain - - name: "arduino:mbed" - # Overwrite the Board Manager installation with the local platform - - source-path: "extras/ArduinoCore-mbed" - name: "arduino:mbed" + platforms: ${{ matrix.board.platforms }} sketch-paths: | ${{ env.UNIVERSAL_SKETCH_PATHS }} enable-deltas-report: 'true' diff --git a/examples/Blocks/Blocks.ino b/examples/Blocks/Blocks.ino deleted file mode 100644 index 85efc0b..0000000 --- a/examples/Blocks/Blocks.ino +++ /dev/null @@ -1,22 +0,0 @@ -/* - * This examples demonstrates the SOURCE/SINK abstraction. - * Each thread may have any number of SOURCES and SINKS that can be connected - * together using the "connectTo" method. - */ - -void setup() { - data_reader.out.connectTo(data_writer.in); - data_reader.start(); - data_writer.start(); - - // put your setup code here, to run once: - pinMode(LEDR, OUTPUT); -} - -void loop() { - // put your main code here, to run repeatedly: - digitalWrite(LEDR, HIGH); - delay(1000); - digitalWrite(LEDR, LOW); - delay(1000); -} diff --git a/examples/Blocks/data_reader.inot b/examples/Blocks/data_reader.inot deleted file mode 100644 index 6bd9708..0000000 --- a/examples/Blocks/data_reader.inot +++ /dev/null @@ -1,12 +0,0 @@ - -/* The output SOURCE, it sends 'int' */ -SOURCE(out, int) - -void setup() { -} - -// a '1' is sent every 100 ms -void loop() { - out.send(1); - delay(100); -} diff --git a/examples/Blocks/data_writer.inot b/examples/Blocks/data_writer.inot deleted file mode 100644 index d3e0e70..0000000 --- a/examples/Blocks/data_writer.inot +++ /dev/null @@ -1,21 +0,0 @@ - -/* - * An 'int' SINK with a size of '0'. This kind of SINK has no buffer so the reading thread - * will block until the writing thread has written something, or vice versa. - */ -SINK(in, int, 0) - -void setup() { - pinMode(LEDB, OUTPUT); -} - -void loop() { - // Read an 'int' from the SINK and discard it. Since there is basically no delay in the loop - // this call will surely block until something comes from the connected SOURCE. In this case - // the pace is dictated by the SOURCE that sends data every 100 ms. - in.read(); - digitalWrite(LEDB, HIGH); - - in.read(); - digitalWrite(LEDB, LOW); -} diff --git a/examples/Demo_Shared_Counter/Consumer.inot b/examples/Demo_Shared_Counter/Consumer.inot new file mode 100644 index 0000000..9d2d190 --- /dev/null +++ b/examples/Demo_Shared_Counter/Consumer.inot @@ -0,0 +1,7 @@ +void setup() { + +} + +void loop() { + Serial.println(counter); +} diff --git a/examples/Demo_Shared_Counter/Demo_Shared_Counter.ino b/examples/Demo_Shared_Counter/Demo_Shared_Counter.ino new file mode 100644 index 0000000..aa71781 --- /dev/null +++ b/examples/Demo_Shared_Counter/Demo_Shared_Counter.ino @@ -0,0 +1,13 @@ +void setup() +{ + Serial.begin(115200); + while (!Serial) { } + + Producer.start(); + Consumer.start(); +} + +void loop() +{ + rtos::ThisThread::yield(); +} diff --git a/examples/Demo_Shared_Counter/Producer.inot b/examples/Demo_Shared_Counter/Producer.inot new file mode 100644 index 0000000..8932873 --- /dev/null +++ b/examples/Demo_Shared_Counter/Producer.inot @@ -0,0 +1,10 @@ +void setup() { + +} + +void loop() { + static int i = 0; + counter = i; + i++; + delay(100); +} diff --git a/examples/Demo_Shared_Counter/SharedVariables.h b/examples/Demo_Shared_Counter/SharedVariables.h new file mode 100644 index 0000000..5891a84 --- /dev/null +++ b/examples/Demo_Shared_Counter/SharedVariables.h @@ -0,0 +1 @@ +SHARED(counter, int); diff --git a/examples/Demo_Source_Sink_Counter/Consumer.inot b/examples/Demo_Source_Sink_Counter/Consumer.inot new file mode 100644 index 0000000..d892c67 --- /dev/null +++ b/examples/Demo_Source_Sink_Counter/Consumer.inot @@ -0,0 +1,11 @@ +SINK(counter, int); + +void setup() +{ + +} + +void loop() +{ + Serial.println(counter.read()); +} diff --git a/examples/Demo_Source_Sink_Counter/Demo_Source_Sink_Counter.ino b/examples/Demo_Source_Sink_Counter/Demo_Source_Sink_Counter.ino new file mode 100644 index 0000000..6ce05a6 --- /dev/null +++ b/examples/Demo_Source_Sink_Counter/Demo_Source_Sink_Counter.ino @@ -0,0 +1,16 @@ +/* + * This examples demonstrates the SOURCE/SINK abstraction. + * Each thread may have any number of SOURCES and SINKS that can be connected + * together using the "connectTo" method. + */ + +void setup() +{ + Producer.counter.connectTo(Consumer.counter); + Producer.start(); + Consumer.start(); +} + +void loop() { + rtos::ThisThread::yield(); +} diff --git a/examples/Demo_Source_Sink_Counter/Producer.inot b/examples/Demo_Source_Sink_Counter/Producer.inot new file mode 100644 index 0000000..fe0a250 --- /dev/null +++ b/examples/Demo_Source_Sink_Counter/Producer.inot @@ -0,0 +1,13 @@ +SOURCE(counter, int); + +void setup() +{ + +} + +void loop() +{ + static int i = 0; + counter.write(i); + i++; +} diff --git a/examples/Blocks/SharedVariables.h b/examples/Demo_Source_Sink_Counter/SharedVariables.h similarity index 100% rename from examples/Blocks/SharedVariables.h rename to examples/Demo_Source_Sink_Counter/SharedVariables.h diff --git a/examples/Demo_Source_Sink_LED/Demo_Source_Sink_LED.ino b/examples/Demo_Source_Sink_LED/Demo_Source_Sink_LED.ino new file mode 100644 index 0000000..d3007b3 --- /dev/null +++ b/examples/Demo_Source_Sink_LED/Demo_Source_Sink_LED.ino @@ -0,0 +1,15 @@ +/* + * This examples demonstrates the SOURCE/SINK abstraction. + * Each thread may have any number of SOURCES and SINKS that can be connected + * together using the "connectTo" method. + */ + +void setup() { + Source_Thread.led.connectTo(Sink_Thread.led); + Sink_Thread.start(); + Source_Thread.start(); +} + +void loop() { + rtos::ThisThread::yield(); +} diff --git a/examples/Demo_Source_Sink_LED/SharedVariables.h b/examples/Demo_Source_Sink_LED/SharedVariables.h new file mode 100644 index 0000000..e69de29 diff --git a/examples/Demo_Source_Sink_LED/Sink_Thread.inot b/examples/Demo_Source_Sink_LED/Sink_Thread.inot new file mode 100644 index 0000000..51f9031 --- /dev/null +++ b/examples/Demo_Source_Sink_LED/Sink_Thread.inot @@ -0,0 +1,21 @@ + +/* + * An 'bool' SINK with a size of '0'. This kind of SINK has no buffer so the reading thread + * will block until the writing thread has written something, or vice versa. + */ +SINK(led, bool); + +void setup() +{ + pinMode(LED_BUILTIN, OUTPUT); +} + +void loop() +{ + /* Read a 'bool' from the SINK and discard it. Since there is basically no delay in the loop + * this call will surely block until something comes from the connected SOURCE. In this case + * the pace is dictated by the SOURCE that sends data every 100 ms. + */ + bool led_on = led.read(); + digitalWrite(LED_BUILTIN, led_on); +} diff --git a/examples/Demo_Source_Sink_LED/Source_Thread.inot b/examples/Demo_Source_Sink_LED/Source_Thread.inot new file mode 100644 index 0000000..1bf7fca --- /dev/null +++ b/examples/Demo_Source_Sink_LED/Source_Thread.inot @@ -0,0 +1,15 @@ +/* The output SOURCE, it sends 'bool' values. */ +SOURCE(led, bool); + +void setup() +{ + +} + +void loop() +{ + led.write(true); + delay(100); + led.write(false); + delay(100); +} diff --git a/examples/Enqueue_test/Enqueue.inot b/examples/Enqueue_test/Enqueue.inot deleted file mode 100644 index f7cfaf4..0000000 --- a/examples/Enqueue_test/Enqueue.inot +++ /dev/null @@ -1,13 +0,0 @@ -void setup() { - // put your setup code here, to run once: - -} - -int i = 0; - -void loop() { - // Continuously pump counter - delay(100); - i++; - counter = i; -} diff --git a/examples/Enqueue_test/Enqueue_test.ino b/examples/Enqueue_test/Enqueue_test.ino deleted file mode 100644 index c0f3b1d..0000000 --- a/examples/Enqueue_test/Enqueue_test.ino +++ /dev/null @@ -1,15 +0,0 @@ -void setup() { - // put your setup code here, to run once: - Serial.begin(115200); - while (!Serial) {} - Enqueue.start(); - Serial.println("start"); -} - -void loop() { - // put your main code here, to run repeatedly: - delay(1000); - for (int i = 0; i < 10; i++) { - Serial.println(counter); - } -} diff --git a/examples/Enqueue_test/SharedVariables.h b/examples/Enqueue_test/SharedVariables.h deleted file mode 100644 index 51ea60e..0000000 --- a/examples/Enqueue_test/SharedVariables.h +++ /dev/null @@ -1 +0,0 @@ -Shared counter; diff --git a/examples/Leds/Leds.ino b/examples/Leds/Leds.ino deleted file mode 100644 index fc6e1fc..0000000 --- a/examples/Leds/Leds.ino +++ /dev/null @@ -1,13 +0,0 @@ -void setup() { - // put your setup code here, to run once: - pinMode(LEDR, OUTPUT); - ledblue.start(); -} - -void loop() { - // put your main code here, to run repeatedly: - digitalWrite(LEDR, HIGH); - delay(1000); - digitalWrite(LEDR, LOW); - delay(1000); -} diff --git a/examples/Leds/SharedVariables.h b/examples/Leds/SharedVariables.h deleted file mode 100644 index 8b13789..0000000 --- a/examples/Leds/SharedVariables.h +++ /dev/null @@ -1 +0,0 @@ - diff --git a/examples/Leds/ledblue.inot b/examples/Leds/ledblue.inot deleted file mode 100644 index 7329782..0000000 --- a/examples/Leds/ledblue.inot +++ /dev/null @@ -1,10 +0,0 @@ -void setup() { - pinMode(LEDB, OUTPUT); -} - -void loop() { - digitalWrite(LEDB, HIGH); - delay(80); - digitalWrite(LEDB, LOW); - delay(100); -} diff --git a/examples/Nano33BLEMegaSketch/Accelerometer.inot b/examples/Nano33BLEMegaSketch/Accelerometer.inot deleted file mode 100644 index 97b28ab..0000000 --- a/examples/Nano33BLEMegaSketch/Accelerometer.inot +++ /dev/null @@ -1,50 +0,0 @@ -/* - Arduino LSM9DS1 - Simple Accelerometer - - This example reads the acceleration values from the LSM9DS1 - sensor and continuously prints them to the Serial Monitor - or Serial Plotter. - - The circuit: - - Arduino Nano 33 BLE Sense - - created 10 Jul 2019 - by Riccardo Rizzo - - This example code is in the public domain. -*/ - -#include - -void setup() { - Serial.begin(9600); - while (!Serial); - Serial.println("Started"); - - if (!IMU.begin()) { - Serial.println("Failed to initialize IMU!"); - while (1); - } - - Serial.print("Accelerometer sample rate = "); - Serial.print(IMU.accelerationSampleRate()); - Serial.println(" Hz"); - Serial.println(); - Serial.println("Acceleration in G's"); - Serial.println("X\tY\tZ"); -} - -void loop() { - float x, y, z; - - if (IMU.accelerationAvailable()) { - IMU.readAcceleration(x, y, z); - - Serial.print(x); - Serial.print('\t'); - Serial.print(y); - Serial.print('\t'); - Serial.println(z); - } - delay(100); -} diff --git a/examples/Nano33BLEMegaSketch/Gesture.inot b/examples/Nano33BLEMegaSketch/Gesture.inot deleted file mode 100644 index 8ece3c5..0000000 --- a/examples/Nano33BLEMegaSketch/Gesture.inot +++ /dev/null @@ -1,84 +0,0 @@ -/* - APDS9960 - All sensor data from APDS9960 - - This example reads all data from the on-board APDS9960 sensor of the - Nano 33 BLE Sense: - - color RGB (red, green, blue) - - proximity - - gesture - and prints updates to the Serial Monitor every 100 ms. - - The circuit: - - Arduino Nano 33 BLE Sense - - This example code is in the public domain. -*/ - -#include - -void setup() { - Serial.begin(9600); - while (!Serial); // Wait for serial monitor to open - - if (!APDS.begin()) { - Serial.println("Error initializing APDS9960 sensor."); - while (true); // Stop forever - } -} - -int proximity = 0; -int r = 0, g = 0, b = 0; -unsigned long lastUpdate = 0; - -void loop() { - - // Check if a proximity reading is available. - if (APDS.proximityAvailable()) { - proximity = APDS.readProximity(); - } - - // check if a gesture reading is available - if (APDS.gestureAvailable()) { - int gesture = APDS.readGesture(); - switch (gesture) { - case GESTURE_UP: - Serial.println("Detected UP gesture"); - break; - - case GESTURE_DOWN: - Serial.println("Detected DOWN gesture"); - break; - - case GESTURE_LEFT: - Serial.println("Detected LEFT gesture"); - break; - - case GESTURE_RIGHT: - Serial.println("Detected RIGHT gesture"); - break; - - default: - // ignore - break; - } - } - - // check if a color reading is available - if (APDS.colorAvailable()) { - APDS.readColor(r, g, b); - } - - // Print updates every 100ms - if (millis() - lastUpdate > 100) { - lastUpdate = millis(); - Serial.print("PR="); - Serial.print(proximity); - Serial.print(" rgb="); - Serial.print(r); - Serial.print(","); - Serial.print(g); - Serial.print(","); - Serial.println(b); - } - delay(320); -} diff --git a/examples/Nano33BLEMegaSketch/Humidity.inot b/examples/Nano33BLEMegaSketch/Humidity.inot deleted file mode 100644 index 890adf5..0000000 --- a/examples/Nano33BLEMegaSketch/Humidity.inot +++ /dev/null @@ -1,45 +0,0 @@ -/* - HTS221 - Read Sensors - - This example reads data from the on-board HTS221 sensor of the - Nano 33 BLE Sense and prints the temperature and humidity sensor - values to the Serial Monitor once a second. - - The circuit: - - Arduino Nano 33 BLE Sense - - This example code is in the public domain. -*/ - -#include - -void setup() { - Serial.begin(9600); - while (!Serial); - - if (!HTS.begin()) { - Serial.println("Failed to initialize humidity temperature sensor!"); - while (1); - } -} - -void loop() { - // read all the sensor values - float temperature = HTS.readTemperature(); - float humidity = HTS.readHumidity(); - - // print each of the sensor values - Serial.print("Temperature = "); - Serial.print(temperature); - Serial.println(" °C"); - - Serial.print("Humidity = "); - Serial.print(humidity); - Serial.println(" %"); - - // print an empty line - Serial.println(); - - // wait 1 second to print again - delay(1000); -} diff --git a/examples/Nano33BLEMegaSketch/Nano33BLEMegaSketch.ino b/examples/Nano33BLEMegaSketch/Nano33BLEMegaSketch.ino deleted file mode 100644 index 538e170..0000000 --- a/examples/Nano33BLEMegaSketch/Nano33BLEMegaSketch.ino +++ /dev/null @@ -1,14 +0,0 @@ -void setup() { - // put your setup code here, to run once: - Serial.begin(115200); - while (!Serial); - Serial.tags(); - Humidity.start(); - Gesture.start(); - Accelerometer.start(); -} - -void loop() { - // put your main code here, to run repeatedly: - -} diff --git a/examples/Nano33BLEMegaSketch/SharedVariables.h b/examples/Nano33BLEMegaSketch/SharedVariables.h deleted file mode 100644 index 8b13789..0000000 --- a/examples/Nano33BLEMegaSketch/SharedVariables.h +++ /dev/null @@ -1 +0,0 @@ - diff --git a/examples/SharedResources/SharedResources.ino b/examples/SharedResources/SharedResources.ino deleted file mode 100644 index 94dbbae..0000000 --- a/examples/SharedResources/SharedResources.ino +++ /dev/null @@ -1,35 +0,0 @@ -void setup() { - Serial.begin(115200); - while (!Serial); - - // Insert tags before Serial transmission to distinguish - // the thread that originated them - Serial.tags(true); - - temp_reader.start(); - pf_reader.start(); - scanner.start(); - ecc_reader.start(); -} - -void loop() { - //Serial.println(temperature); // blocks until new data is available - //Serial.println(temperature.peek()); // returns immediately the last known value - - struct i2cScanResults results = scanResults; - for (int i = 0; i < 128; i++) { - if (results.address[i] == true) { - Serial.println("0x" + String(i, HEX)); - } - } - - if (Serial.available()) { - Serial.println("Got something"); - while (Serial.available()) { - Serial.write(Serial.read()); - } - } - - Serial.println("PF1550 ID: " + String(pf1550_id, HEX)); - Serial.println("ECC608 ID: " + String(ecc608_id, HEX)); -} diff --git a/examples/SharedResources/SharedVariables.h b/examples/SharedResources/SharedVariables.h deleted file mode 100644 index 51b8363..0000000 --- a/examples/SharedResources/SharedVariables.h +++ /dev/null @@ -1,12 +0,0 @@ -// The preprocessor should enforce that all variables belonging here are declared Shared -// There's no risk to spill other variables around since they are all private members of the automatic class - -struct i2cScanResults { - uint8_t address[128]; -}; - -Shared temperature; - -Shared scanResults; -Shared pf1550_id; -Shared ecc608_id; diff --git a/examples/SharedResources/ecc_reader.inot b/examples/SharedResources/ecc_reader.inot deleted file mode 100644 index 4ec4317..0000000 --- a/examples/SharedResources/ecc_reader.inot +++ /dev/null @@ -1,15 +0,0 @@ -void setup() { - Wire.begin(); - Wire.setClock(100000); -} - -void loop() { - Wire.beginTransmission(0x60); - Wire.write((uint8_t)0x0); - Wire.endTransmission(false); - int res = Wire.requestFrom(0x60, 1); - delay(random() % 5); - if (res == 1) { - ecc608_id = Wire.read(); - } -} diff --git a/examples/SharedResources/pf_reader.inot b/examples/SharedResources/pf_reader.inot deleted file mode 100644 index f69c8b0..0000000 --- a/examples/SharedResources/pf_reader.inot +++ /dev/null @@ -1,21 +0,0 @@ -void setup() { - Serial.begin(115200); - Wire.begin(); - Wire.setClock(400000); -} - -void loop() { - Wire.beginTransmission(0x8); - Wire.write((uint8_t)0x0); - Wire.endTransmission(false); - Wire.requestFrom(0x8, 1); - delay(random() % 60); - pf1550_id = Wire.read(); - - if (Serial.available()) { - Serial.println("Got something"); - while (Serial.available()) { - Serial.write(Serial.read()); - } - } -} diff --git a/examples/SharedResources/scanner.inot b/examples/SharedResources/scanner.inot deleted file mode 100644 index 27b50aa..0000000 --- a/examples/SharedResources/scanner.inot +++ /dev/null @@ -1,14 +0,0 @@ -void setup() { - Wire.begin(); - Wire.setClock(100000); -} - -void loop() { - struct i2cScanResults results = {}; - for (int i = 0; i < 128; i++) { - Wire.beginTransmission (i); - results.address[i] = (Wire.endTransmission () == 0); - } - scanResults = results; - delay(random() % 1000); -} diff --git a/examples/SharedResources/temp_reader.inot b/examples/SharedResources/temp_reader.inot deleted file mode 100644 index 1d5aac9..0000000 --- a/examples/SharedResources/temp_reader.inot +++ /dev/null @@ -1,11 +0,0 @@ -// cannot include anything since we are going to be wrapped in a class -// the preprocessor should move the includes in another file - -void setup() { - //initialize -} - -void loop() { - temperature = random() * 1.0f; - delay(random() % 1000); -} diff --git a/examples/SharedResourcesVariousLibraries/Accelerometer.inot b/examples/SharedResourcesVariousLibraries/Accelerometer.inot deleted file mode 100644 index e0192e2..0000000 --- a/examples/SharedResourcesVariousLibraries/Accelerometer.inot +++ /dev/null @@ -1,58 +0,0 @@ -/* - Arduino LSM9DS1 - Simple Accelerometer - - This example reads the acceleration values from the LSM9DS1 - sensor and continuously prints them to the Serial Monitor - or Serial Plotter. - - The circuit: - - Arduino Nano 33 BLE Sense - - created 10 Jul 2019 - by Riccardo Rizzo - - This example code is in the public domain. -*/ - -#include - -LSM9DS1Class myIMU = LSM9DS1Class(Wire1); -// Note: classic -// LSM9DS1Class myIMU(Wire1); -// cannot be used here since it becomes a private member of the class. -// The compilation would fail with "error: 'Wire1' is not a type" (whatever this means) -// This only happen when we reinstantiate a singleton because we want to change something in its constructor - -void setup() { - Serial.begin(9600); - while (!Serial); - Serial.println("Started"); - - if (!myIMU.begin()) { - Serial.println("Failed to initialize IMU!"); - while (1) { - delay(1000); - } - } - - Serial.print("Accelerometer sample rate = "); - Serial.print(myIMU.accelerationSampleRate()); - Serial.println(" Hz"); - Serial.println(); - Serial.println("Acceleration in G's"); - Serial.println("X\tY\tZ"); -} - -void loop() { - float x, y, z; - - if (myIMU.accelerationAvailable()) { - myIMU.readAcceleration(x, y, z); - - Serial.print(x); - Serial.print('\t'); - Serial.print(y); - Serial.print('\t'); - Serial.println(z); - } -} diff --git a/examples/SharedResourcesVariousLibraries/GetRandom.inot b/examples/SharedResourcesVariousLibraries/GetRandom.inot deleted file mode 100644 index 421577c..0000000 --- a/examples/SharedResourcesVariousLibraries/GetRandom.inot +++ /dev/null @@ -1,18 +0,0 @@ -#include "ArduinoECCX08.h" - -void setup() { - int ret = ECCX08.begin(); - if (!ret) { - Serial.println("ECCX08 could not start"); - } -} - -int _randomNumber; - -void loop() { - randomNumber = ECCX08.random(65535); - // ATTENTION: if we write `delay(randomNumber % 60);` - // the call becomes blocking and it only gets read in the same thread - // so we have full starvation (two consumers) - delay(randomNumber.latest() % 60); -} diff --git a/examples/SharedResourcesVariousLibraries/SerialReader.inot b/examples/SharedResourcesVariousLibraries/SerialReader.inot deleted file mode 100644 index d13e7db..0000000 --- a/examples/SharedResourcesVariousLibraries/SerialReader.inot +++ /dev/null @@ -1,14 +0,0 @@ -#include "Wire.h" - -void setup() { - Serial.begin(115200); -} - -void loop() { - if (Serial.available()) { - Serial.println("Got something"); - while (Serial.available()) { - Serial.write(Serial.read()); - } - } -} diff --git a/examples/SharedResourcesVariousLibraries/SharedResourcesVariousLibraries.ino b/examples/SharedResourcesVariousLibraries/SharedResourcesVariousLibraries.ino deleted file mode 100644 index 339dd60..0000000 --- a/examples/SharedResourcesVariousLibraries/SharedResourcesVariousLibraries.ino +++ /dev/null @@ -1,31 +0,0 @@ -void setup() { - Serial.begin(115200); - while (!Serial); - - // Insert tags before Serial transmission to distinguish - // the thread that originated them - Serial.tags(true); - - // By declaring a serial "raw" we ask the dispatcher to flush - // every N milliseconds without waiting for EOL (for this thread only) - //Serial.raw(); - - TempReader.start(); - SerialReader.start(); - GetRandom.start(); - Accelerometer.start(); -} - -void loop() { - //Serial.println(temperature); // blocks until new data is available - Serial.println(temperature.peek()); // returns immediately the last known value - - if (Serial.available()) { - Serial.println("Got something"); - while (Serial.available()) { - Serial.write(Serial.read()); - } - } - - Serial.println("ECC608 random number: " + String(randomNumber)); -} diff --git a/examples/SharedResourcesVariousLibraries/SharedVariables.h b/examples/SharedResourcesVariousLibraries/SharedVariables.h deleted file mode 100644 index ca037c5..0000000 --- a/examples/SharedResourcesVariousLibraries/SharedVariables.h +++ /dev/null @@ -1,12 +0,0 @@ -// The preprocessor should enforce that all variables belonging here are declared Shared -// There's no risk to spill other variables around since they are all private members of the automatic class - -struct i2cScanResults { - uint8_t address[128]; -}; - -Shared temperature; - -Shared scanResults; -Shared pf1550_id; -Shared randomNumber; diff --git a/examples/SharedResourcesVariousLibraries/TempReader.inot b/examples/SharedResourcesVariousLibraries/TempReader.inot deleted file mode 100644 index ad21d03..0000000 --- a/examples/SharedResourcesVariousLibraries/TempReader.inot +++ /dev/null @@ -1,115 +0,0 @@ -#include - -// OneWire DS18S20, DS18B20, DS1822 Temperature Example -// -// http://www.pjrc.com/teensy/td_libs_OneWire.html -// -// The DallasTemperature library can do all this work for you! -// https://github.com/milesburton/Arduino-Temperature-Control-Library - -OneWire ds = OneWire(10); // on pin 10 (a 4.7K resistor is necessary) -// See note on Accelerometer.inot - -void setup(void) { - Serial.begin(9600); -} - -void loop(void) { - byte i; - byte present = 0; - byte type_s; - byte data[12]; - byte addr[8]; - float celsius, fahrenheit; - - if ( !ds.search(addr)) { - Serial.println("No more addresses."); - Serial.println(); - ds.reset_search(); - delay(250); - return; - } - - Serial.print("ROM ="); - for( i = 0; i < 8; i++) { - Serial.write(' '); - Serial.print(addr[i], HEX); - } - - if (OneWire::crc8(addr, 7) != addr[7]) { - Serial.println("CRC is not valid!"); - return; - } - Serial.println(); - - // the first ROM byte indicates which chip - switch (addr[0]) { - case 0x10: - Serial.println(" Chip = DS18S20"); // or old DS1820 - type_s = 1; - break; - case 0x28: - Serial.println(" Chip = DS18B20"); - type_s = 0; - break; - case 0x22: - Serial.println(" Chip = DS1822"); - type_s = 0; - break; - default: - Serial.println("Device is not a DS18x20 family device."); - return; - } - - ds.reset(); - ds.select(addr); - ds.write(0x44, 1); // start conversion, with parasite power on at the end - - delay(1000); // maybe 750ms is enough, maybe not - // we might do a ds.depower() here, but the reset will take care of it. - - present = ds.reset(); - ds.select(addr); - ds.write(0xBE); // Read Scratchpad - - Serial.print(" Data = "); - Serial.print(present, HEX); - Serial.print(" "); - for ( i = 0; i < 9; i++) { // we need 9 bytes - data[i] = ds.read(); - Serial.print(data[i], HEX); - Serial.print(" "); - } - Serial.print(" CRC="); - Serial.print(OneWire::crc8(data, 8), HEX); - Serial.println(); - - // Convert the data to actual temperature - // because the result is a 16 bit signed integer, it should - // be stored to an "int16_t" type, which is always 16 bits - // even when compiled on a 32 bit processor. - int16_t raw = (data[1] << 8) | data[0]; - if (type_s) { - raw = raw << 3; // 9 bit resolution default - if (data[7] == 0x10) { - // "count remain" gives full 12 bit resolution - raw = (raw & 0xFFF0) + 12 - data[6]; - } - } else { - byte cfg = (data[4] & 0x60); - // at lower res, the low bits are undefined, so let's zero them - if (cfg == 0x00) raw = raw & ~7; // 9 bit resolution, 93.75 ms - else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms - else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms - //// default is 12 bit resolution, 750 ms conversion time - } - celsius = (float)raw / 16.0; - fahrenheit = celsius * 1.8 + 32.0; - Serial.print(" Temperature = "); - Serial.print(celsius); - Serial.print(" Celsius, "); - Serial.print(fahrenheit); - Serial.println(" Fahrenheit"); - - temperature = celsius; -} diff --git a/examples/i2c_concurrent/SharedVariables.h b/examples/i2c_concurrent/SharedVariables.h deleted file mode 100644 index 51b8363..0000000 --- a/examples/i2c_concurrent/SharedVariables.h +++ /dev/null @@ -1,12 +0,0 @@ -// The preprocessor should enforce that all variables belonging here are declared Shared -// There's no risk to spill other variables around since they are all private members of the automatic class - -struct i2cScanResults { - uint8_t address[128]; -}; - -Shared temperature; - -Shared scanResults; -Shared pf1550_id; -Shared ecc608_id; diff --git a/examples/i2c_concurrent/ecc_reader.inot b/examples/i2c_concurrent/ecc_reader.inot deleted file mode 100644 index 4ec4317..0000000 --- a/examples/i2c_concurrent/ecc_reader.inot +++ /dev/null @@ -1,15 +0,0 @@ -void setup() { - Wire.begin(); - Wire.setClock(100000); -} - -void loop() { - Wire.beginTransmission(0x60); - Wire.write((uint8_t)0x0); - Wire.endTransmission(false); - int res = Wire.requestFrom(0x60, 1); - delay(random() % 5); - if (res == 1) { - ecc608_id = Wire.read(); - } -} diff --git a/examples/i2c_concurrent/i2c_concurrent.ino b/examples/i2c_concurrent/i2c_concurrent.ino deleted file mode 100644 index 9e19675..0000000 --- a/examples/i2c_concurrent/i2c_concurrent.ino +++ /dev/null @@ -1,57 +0,0 @@ -#include "Wire.h" -#include "SerialDispatcher.h" - -/* this part should be autogenerated */ -/* -THD_ENTER(temp_reader) -#include "temp_reader.h" -THD_DONE(temp_reader) - -// A singleton called temp_reader_obj is automatically created but the user can still -// instantiate another with -// temp_reader_class myObj; - -THD_ENTER(pf_reader) -#include "pf_reader.h" -THD_DONE(pf_reader) - -THD_ENTER(ecc_reader) -#include "ecc_reader.h" -THD_DONE(ecc_reader) - -THD_ENTER(scanner) -#include "scanner.h" -THD_DONE(scanner) -*/ - -void setup() { - Serial.begin(115200); - while (!Serial); - Serial.tags(true); - temp_reader.start(); - pf_reader.start(); - scanner.start(); - ecc_reader.start(); -} - -void loop() { - //Serial.println(temperature); // blocks until new data is available - //Serial.println(temperature.peek()); // returns immediately the last known value - - struct i2cScanResults results = scanResults; - for (int i = 0; i < 128; i++) { - if (results.address[i] == true) { - Serial.println("0x" + String(i, HEX)); - } - } - - if (Serial.available()) { - Serial.println("Got something"); - while (Serial.available()) { - Serial.write(Serial.read()); - } - } - - Serial.println("PF1550 ID: " + String(pf1550_id, HEX)); - Serial.println("ECC608 ID: " + String(ecc608_id, HEX)); -} diff --git a/examples/i2c_concurrent/pf_reader.inot b/examples/i2c_concurrent/pf_reader.inot deleted file mode 100644 index 4a8ad80..0000000 --- a/examples/i2c_concurrent/pf_reader.inot +++ /dev/null @@ -1,23 +0,0 @@ -void setup() { - Serial.begin(115200); - Wire.begin(); - Wire.setClock(400000); -} - -void loop() { - Wire.beginTransmission(0x8); - Wire.write((uint8_t)0x0); - Wire.endTransmission(false); - Wire.requestFrom(0x8, 1); - delay(random() % 60); - pf1550_id = Wire.read(); - - Serial.println("I'm pfreader"); - - if (Serial.available()) { - Serial.println("Got something"); - while (Serial.available()) { - Serial.write(Serial.read()); - } - } -} diff --git a/examples/i2c_concurrent/scanner.inot b/examples/i2c_concurrent/scanner.inot deleted file mode 100644 index 27b50aa..0000000 --- a/examples/i2c_concurrent/scanner.inot +++ /dev/null @@ -1,14 +0,0 @@ -void setup() { - Wire.begin(); - Wire.setClock(100000); -} - -void loop() { - struct i2cScanResults results = {}; - for (int i = 0; i < 128; i++) { - Wire.beginTransmission (i); - results.address[i] = (Wire.endTransmission () == 0); - } - scanResults = results; - delay(random() % 1000); -} diff --git a/examples/i2c_concurrent/temp_reader.inot b/examples/i2c_concurrent/temp_reader.inot deleted file mode 100644 index 1d5aac9..0000000 --- a/examples/i2c_concurrent/temp_reader.inot +++ /dev/null @@ -1,11 +0,0 @@ -// cannot include anything since we are going to be wrapped in a class -// the preprocessor should move the includes in another file - -void setup() { - //initialize -} - -void loop() { - temperature = random() * 1.0f; - delay(random() % 1000); -} diff --git a/examples/i2c_concurrent/temp_reader_complete.inot b/examples/i2c_concurrent/temp_reader_complete.inot deleted file mode 100644 index bcf05e4..0000000 --- a/examples/i2c_concurrent/temp_reader_complete.inot +++ /dev/null @@ -1,27 +0,0 @@ -class temp_reader_class { - - Shared temperature; - void setup() { - //initialize - } - - void loop() { - temperature = random() * 1.0f; - delay(1000); - } - - void temp_reader_execute() { - setup(); - while (1) { - loop(); - } - } - - rtos::Thread t; - public: - void start() { - t.start(mbed::callback(this, &temp_reader_class::temp_reader_execute)); - } -}; - -temp_reader_class temp_reader_obj; diff --git a/src/Arduino_Threads.cpp b/src/Arduino_Threads.cpp new file mode 100644 index 0000000..444ded5 --- /dev/null +++ b/src/Arduino_Threads.cpp @@ -0,0 +1,125 @@ +/* + * This file is part of the Arduino_ThreadsafeIO library. + * Copyright (c) 2021 Arduino SA. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include "Arduino_Threads.h" + +/************************************************************************************** + * STATIC MEMBER DECLARATION + **************************************************************************************/ + +rtos::EventFlags Arduino_Threads::_global_events; + +/************************************************************************************** + * CTOR/DTOR + **************************************************************************************/ + +Arduino_Threads::Arduino_Threads() +: _start_flags{0} +, _stop_flags{0} +, _loop_delay_ms{0} +{ + +} + +Arduino_Threads::~Arduino_Threads() +{ + terminate(); +} + +/************************************************************************************** + * PUBLIC MEMBER FUNCTIONS + **************************************************************************************/ + +void Arduino_Threads::start(int const stack_size, uint32_t const start_flags, uint32_t const stop_flags) +{ + _start_flags = start_flags; + _stop_flags = stop_flags; + _thread.reset(new rtos::Thread(osPriorityNormal, stack_size, nullptr, _tabname)); + _thread->start(mbed::callback(this, &Arduino_Threads::threadFunc)); +} + +void Arduino_Threads::terminate() +{ + _thread->terminate(); + _thread->join(); +} + +void Arduino_Threads::sendEvent(uint32_t const event) +{ + _thread->flags_set(event); +} + +void Arduino_Threads::setLoopDelay(uint32_t const delay) +{ + _loop_delay_ms = delay; +} + +void Arduino_Threads::broadcastEvent(uint32_t const event) +{ + _global_events.set(event); +} + +/************************************************************************************** + * PRIVATE MEMBER FUNCTIONS + **************************************************************************************/ + +void Arduino_Threads::threadFunc() +{ + setup(); + /* If _start_flags have been passed then wait until all the flags are set + * before starting the loop. this is used to synchronize loops from multiple + * sketches. + */ + if (_start_flags != 0) + _global_events.wait_all(_start_flags); + + /* if _stop_flags have been passed stop when all the flags are set + * otherwise loop forever + */ + for (;;) + { + loop(); + /* On exit clear the flags that have forced us to stop. + * note that if two groups of sketches stop on common flags + * the first group will clear them so the second group may never + * exit. + */ + if (_stop_flags!=0) + { + if ((_global_events.get() & _stop_flags) != _stop_flags) + { + _global_events.clear(_stop_flags); + return; + } + + if ((rtos::ThisThread::flags_get() & _stop_flags) != _stop_flags) + { + rtos::ThisThread::flags_clear(_stop_flags); + return; + } + } + + /* Sleep for the time we've been asked to insert between loops. + */ + rtos::ThisThread::sleep_for(rtos::Kernel::Clock::duration_u32(_loop_delay_ms)); + } +} diff --git a/src/Arduino_Threads.h b/src/Arduino_Threads.h index f176365..ca9250f 100644 --- a/src/Arduino_Threads.h +++ b/src/Arduino_Threads.h @@ -1,16 +1,47 @@ +/* + * This file is part of the Arduino_ThreadsafeIO library. + * Copyright (c) 2021 Arduino SA. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + #ifndef ARDUINO_THREADS_H_ #define ARDUINO_THREADS_H_ -#include +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include +#include + +#include "Sink.hpp" +#include "Source.hpp" +#include "Shared.hpp" + +/************************************************************************************** + * DEFINE + **************************************************************************************/ #define SOURCE(name, type) \ public: \ Source name; \ private: -#define SINK(name, type, size) \ +#define SINK(name, type) \ public: \ - Sink name{size}; \ + SinkBlocking name; \ private: // we need to call the Sink(int size) non-default constructor using size as parameter. // This is done by writing @@ -22,281 +53,55 @@ public: \ // This is called "C++11 uniform init" (using "{}" instead of "()" without "="... yikes!) // https://chromium.googlesource.com/chromium/src/+/master/styleguide/c++/c++-dos-and-donts.md -// Forward declaration of Sink and Source -template -class Sink; -template -class Source; - -template -class Sink -{ - private: - rtos::Mutex dataMutex; - rtos::ConditionVariable dataAvailable; - rtos::ConditionVariable slotAvailable; - T latest; - Sink *next; - const int size; - int first, last; - bool full; - T *queue; - - public: - Sink(int s) : - dataAvailable(dataMutex), - slotAvailable(dataMutex), - size(s), - queue((size > 0) ? new T[size] : nullptr), - first(0), last(0), full(false) - {}; - - ~Sink() { - if (queue != nullptr) { delete queue; } - } - +#define SHARED(name, type) \ + Shared name; - //protected: TODO - void connectTo(Sink &sink) - { - if (next == nullptr) { - next = &sink; - } else { - next->connectTo(sink); - } - } +/************************************************************************************** + * CLASS DECLARATION + **************************************************************************************/ - T read() - { - // Non-blocking shared variable - if (size == -1) { - dataMutex.lock(); - T res = latest; - dataMutex.unlock(); - return res; - } - - // Blocking shared variable - if (size == 0) { - dataMutex.lock(); - while (!full) { - dataAvailable.wait(); - } - T res = latest; - full = false; - slotAvailable.notify_all(); - dataMutex.unlock(); - return res; - } - - // Blocking queue - dataMutex.lock(); - while (first == last && !full) { - dataAvailable.wait(); - } - T res = queue[first++]; - first %= size; - if (full) { - full = false; - slotAvailable.notify_one(); - } - dataMutex.unlock(); - return res; - } - - //protected: TODO - void inject(const T &value) - { - dataMutex.lock(); - - // Non-blocking shared variable - if (size == -1) { - latest = value; - } - - // Blocking shared variable - else if (size == 0) { - while (full) { - slotAvailable.wait(); - } - latest = value; - full = true; - dataAvailable.notify_one(); - slotAvailable.wait(); - } +#define CONCAT2(x,y) x##y +#define CONCAT(x,y) CONCAT2(x,y) - // Blocking queue - else { - while (full) { - slotAvailable.wait(); - } - if (first == last) { - dataAvailable.notify_one(); - } - queue[last++] = value; - last %= size; - if (first == last) { - full = true; - } - } - dataMutex.unlock(); +#define INCF(F) INCF_(F) +#define INCF_(F) #F - if (next) next->inject(value); - } -}; +#define _macroToString(sequence) #sequence -template -class Source +class Arduino_Threads { - public: - Source() {}; - - void connectTo(Sink &sink) { - if (destination == nullptr) { - destination = &sink; - } else { - destination->connectTo(sink); - } - } +public: - void send(const T &value) { - if (destination) destination->inject(value); - } + Arduino_Threads(); + virtual ~Arduino_Threads(); - private: - Sink *destination; -}; - -template -class Shared // template definition -{ - public: - Shared() { - } - operator T() { - osEvent evt = queue.get(); - if (evt.status == osEventMessage) { - /* Obtain the oldest inserted element from the queue. */ - T * val_ptr = reinterpret_cast(evt.value.p); - /* Copy the content of T stored in the memory pool since we'll have to free the memory pool afterwards. */ - T const tmp_val = *val_ptr; - /* Free the allocated memory in the memory pool. */ - memory_pool.free(val_ptr); - /* Return obtained value from queue. */ - return tmp_val; - } - return val; - } - T& operator= (const T& other) { - if (queue.full()) { - // invokes operator T() to discard oldest element and free its memory - T discard = *this; - } - val = other; - /* Allocate memory in the memory pool. */ - T * val_ptr = memory_pool.alloc(); - /* Copy the content of 'other' into the freshly allocated message. */ - *val_ptr = other; - /* Insert into queue. */ - queue.put(val_ptr); - return (*val_ptr); - } - T& peek() { - return val; - } - T& latest() { - return peek(); - } - private: - T val; - rtos::MemoryPool memory_pool; - rtos::Queue queue; -}; + void start (int const stack_size = 4096, uint32_t const start_flags = 0, uint32_t const stop_flags = 0); + void terminate (); + void setLoopDelay(uint32_t const delay); + void sendEvent (uint32_t const event); -#define CONCAT2(x,y) x##y -#define CONCAT(x,y) CONCAT2(x,y) + static void broadcastEvent(uint32_t event); -#define INCF(F) INCF_(F) -#define INCF_(F) #F -#define _macroToString(sequence) #sequence +protected: + char * _tabname; -class ArduinoThreads { - private: - static rtos::EventFlags globalEvents; - uint32_t startFlags; - uint32_t stopFlags; - uint32_t loopDelay; - virtual void setup(void) {}; - virtual void loop(void) {}; - void execute() { - setup(); - // if startFlags have been passed then wait until all the flags are set - // before starting the loop. this is used to synchronize loops from multiple - // sketches. - if (startFlags != 0) { - globalEvents.wait_all(startFlags); - } + virtual void setup() = 0; + virtual void loop () = 0; - // if stopFlags have been passed stop when all the flags are set - // otherwise loop forever - while ( 1 ) { - loop(); - // on exit clear the flags that have forced us to stop. - // note that if two groups of sketches stop on common flags - // the first group will clear them so the second group may never - // exit - if (stopFlags!=0) { - if ((globalEvents.get()&stopFlags)!=stopFlags) { - globalEvents.clear(stopFlags); - return; - } - if ((rtos::ThisThread::flags_get()&stopFlags)!=stopFlags) { - rtos::ThisThread::flags_clear(stopFlags); - return; - } - } - // sleep for the time we've been asked to insert between loops - rtos::ThisThread::sleep_for(loopDelay); - } - } - rtos::Thread *t; +private: - protected: - char* _tabname; + static rtos::EventFlags _global_events; + mbed::SharedPtr _thread; + uint32_t _start_flags, _stop_flags; + uint32_t _loop_delay_ms; - public: - // start this sketch - void start(int stacksize = 4096, uint32_t startFlags=0, uint32_t stopFlags=0) { - this->startFlags = startFlags; - this->stopFlags = stopFlags; - loopDelay=0; - t = new rtos::Thread(osPriorityNormal, stacksize, nullptr, _tabname); - t->start(mbed::callback(this, &ArduinoThreads::execute)); - } - // kill this sketch - void terminate() { - t->terminate(); - } - // send an event to all sketches at the same time - static void broadcastEvent(uint32_t event) { - globalEvents.set(event); - } - // send an event only to this sketch - void sendEvent(uint32_t event) { - t->flags_set(event); - } - // set the rate at which loop function will be called - void setLoopDelay(uint32_t delay) { - loopDelay = delay; - } + void threadFunc(); }; -rtos::EventFlags ArduinoThreads::globalEvents; - -#define THD_ENTER(tabname) class CONCAT(tabname, Class) : public ArduinoThreads { \ +#define THD_ENTER(tabname) class CONCAT(tabname, Class) : public Arduino_Threads { \ public: \ CONCAT(tabname, Class)() { _tabname = _macroToString(tabname); } \ private: \ @@ -305,7 +110,4 @@ private: \ }; \ CONCAT(tabname,Class) tabname; -#include "Wire.h" -#include "SerialDispatcher.h" - #endif /* ARDUINO_THREADS_H_ */ diff --git a/src/Serial.cpp b/src/Serial.cpp deleted file mode 100644 index 9553c59..0000000 --- a/src/Serial.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "SerialDispatcher.h" - -SerialClassDispatcher Serial(SerialUSB); \ No newline at end of file diff --git a/src/SerialDispatcher.h b/src/SerialDispatcher.h deleted file mode 100644 index 43acedf..0000000 --- a/src/SerialDispatcher.h +++ /dev/null @@ -1,199 +0,0 @@ -#ifndef __SERIAL_DISPATCHER_H__ -#define __SERIAL_DISPATCHER_H__ - -#include "Arduino.h" -#undef Serial -#include "api/RingBuffer.h" - -struct _sinkBuffers { - osThreadId_t id; - bool reader; - bool raw; - rtos::Semaphore* sem; - RingBuffer rxBuffer; - RingBuffer txBuffer; -}; - -#define READ_READY_UNBLOCK (1 << 1) - -class SerialClassDispatcher : public HardwareSerial { - public: - SerialClassDispatcher(HardwareSerial& _serial) : serial(_serial) { - - } - void begin(unsigned long baudrate) { - if (!begun) { - serial.begin(baudrate); - begun = true; - printer.start(mbed::callback(this, &SerialClassDispatcher::timedPrint)); - } - sinkBuffers[users].id = rtos::ThisThread::get_id(); - sinkBuffers[users].sem = new rtos::Semaphore(1); - sinkBuffers[users].raw = false; - users++; - } - - void begin(unsigned long baudrate, uint16_t config) { - if (!begun) { - serial.begin(baudrate, config); - begun = true; - } - sinkBuffers[users].id = rtos::ThisThread::get_id(); - sinkBuffers[users].sem = new rtos::Semaphore(1); - sinkBuffers[users].raw = false; - users++; - } - - using Print::write; // pull in write(str) and write(buf, size) from Print - - operator bool() { - return serial; - } - - void tags(bool enable = true) { - print_tags = enable; - } - - void end() { - if (users == 0) { - serial.end(); - } else { - users--; - } - } - - size_t write(uint8_t data) { - findSemaphore(rtos::ThisThread::get_id())->acquire(); - findThreadTxBuffer(rtos::ThisThread::get_id()).store_char(data); - findSemaphore(rtos::ThisThread::get_id())->release(); - unlock_print.set(READ_READY_UNBLOCK); - } - - size_t write(const uint8_t* data, size_t len) { - findSemaphore(rtos::ThisThread::get_id())->acquire(); - for (int i=0; irelease(); - unlock_print.set(READ_READY_UNBLOCK); - } - - void flushReadBuffer() { - while (serial.available()) { - int c = serial.read(); - for (int i = 0; i < users; i++) { - if (sinkBuffers[i].reader) { - sinkBuffers[i].rxBuffer.store_char(c); - } - } - } - } - - int read() { - *isReader(rtos::ThisThread::get_id()) = true; - flushReadBuffer(); - return findThreadRxBuffer(rtos::ThisThread::get_id()).read_char(); - } - - int peek() { - *isReader(rtos::ThisThread::get_id()) = true; - flushReadBuffer(); - return findThreadRxBuffer(rtos::ThisThread::get_id()).peek(); - } - - void raw(bool _raw = true) { - *isRaw(rtos::ThisThread::get_id()) = _raw; - } - - void flush() { - serial.flush(); - } - - int available() { - *isReader(rtos::ThisThread::get_id()) = true; - flushReadBuffer(); - return findThreadRxBuffer(rtos::ThisThread::get_id()).available(); - } - - private: - - void timedPrint() { - while (1) { - unlock_print.wait_any(READ_READY_UNBLOCK, osWaitForever, true); - for (int i = 0; i < users; i++) { - sinkBuffers[i].sem->acquire(); - // Implementation "leak", should be changed at RingBuffer API level - int c = sinkBuffers[i].txBuffer._iHead == 0 ? - sinkBuffers[i].txBuffer._aucBuffer[sizeof(sinkBuffers[i].txBuffer._aucBuffer) -1] : - sinkBuffers[i].txBuffer._aucBuffer[sinkBuffers[i].txBuffer._iHead - 1]; - if ((!sinkBuffers[i].raw && (c == '\n' /*|| c == '\r' */|| c == '\0')) || - sinkBuffers[i].raw || !sinkBuffers[i].txBuffer.availableForStore()) { - if (sinkBuffers[i].txBuffer.available() && print_tags) { - serial.print("["); - serial.print(i); - serial.print("] "); - } - while (sinkBuffers[i].txBuffer.available()) { - serial.write(sinkBuffers[i].txBuffer.read_char()); - } - } - sinkBuffers[i].sem->release(); - } - } - } - - bool* isReader(osThreadId_t id) { - for (int i = 0; i < 10; i++) { - if (id == sinkBuffers[i].id) { - return &sinkBuffers[i].reader; - } - } - } - - bool* isRaw(osThreadId_t id) { - for (int i = 0; i < 10; i++) { - if (id == sinkBuffers[i].id) { - return &sinkBuffers[i].raw; - } - } - } - - rtos::Semaphore* findSemaphore(osThreadId_t id) { - for (int i = 0; i < 10; i++) { - if (id == sinkBuffers[i].id) { - return sinkBuffers[i].sem; - } - } - } - - RingBuffer& findThreadTxBuffer(osThreadId_t id) { - for (int i = 0; i < 10; i++) { - if (id == sinkBuffers[i].id) { - return sinkBuffers[i].txBuffer; - } - } - } - - RingBuffer& findThreadRxBuffer(osThreadId_t id) { - for (int i = 0; i < 10; i++) { - if (id == sinkBuffers[i].id) { - return sinkBuffers[i].rxBuffer; - } - } - } - - private: - HardwareSerial& serial; - int users = 0; - bool begun = false; - int currentClock = 400000; - struct _sinkBuffers sinkBuffers[10]; - osThreadId_t currentThread; - rtos::Thread printer; - rtos::EventFlags unlock_print; - bool print_tags = false; -}; - -extern SerialClassDispatcher Serial; - -#endif \ No newline at end of file diff --git a/src/Shared.hpp b/src/Shared.hpp new file mode 100644 index 0000000..8b35cc9 --- /dev/null +++ b/src/Shared.hpp @@ -0,0 +1,95 @@ +/* + * This file is part of the Arduino_ThreadsafeIO library. + * Copyright (c) 2021 Arduino SA. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ARDUINO_THREADS_SHARED_HPP_ +#define ARDUINO_THREADS_SHARED_HPP_ + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include + +/************************************************************************************** + * CONSTANT + **************************************************************************************/ + +static size_t constexpr SHARED_QUEUE_SIZE = 16; + +/************************************************************************************** + * CLASS DECLARATION + **************************************************************************************/ + +template +class Shared +{ +public: + + operator T(); + void operator = (T const & other); + inline T peek() const { return _val; } + + +private: + + T _val; + rtos::Mail _mailbox; + +}; + +/************************************************************************************** + * PUBLIC MEMBER FUNCTIONS + **************************************************************************************/ + +template +Shared::operator T() +{ + T * val_ptr = _mailbox.try_get_for(rtos::Kernel::wait_for_u32_forever); + if (val_ptr) + { + T const tmp_val = *val_ptr; + _mailbox.free(val_ptr); + return tmp_val; + } + return _val; +} + +template +void Shared::operator = (T const & other) +{ + /* If the mailbox is full we are discarding the + * oldest element and then push the new one into + * the queue. + **/ + if (_mailbox.full()) + { + T * val_ptr = _mailbox.try_get_for(rtos::Kernel::wait_for_u32_forever); + _mailbox.free(val_ptr); + } + + _val = other; + + T * val_ptr = _mailbox.try_alloc(); + if (val_ptr) + { + *val_ptr = other; + _mailbox.put(val_ptr); + } +} + +#endif /* ARDUINO_THREADS_SHARED_HPP_ */ diff --git a/src/Sink.hpp b/src/Sink.hpp new file mode 100644 index 0000000..40179a2 --- /dev/null +++ b/src/Sink.hpp @@ -0,0 +1,140 @@ +/* + * This file is part of the Arduino_ThreadsafeIO library. + * Copyright (c) 2021 Arduino SA. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ARDUINO_THREADS_SINK_HPP_ +#define ARDUINO_THREADS_SINK_HPP_ + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include + +/************************************************************************************** + * CLASS DECLARATION + **************************************************************************************/ + +template +class SinkBase +{ +public: + + virtual ~SinkBase() { } + + virtual T read() = 0; + virtual void inject(T const & value) = 0; +}; + +template +class SinkNonBlocking : public SinkBase +{ +public: + + SinkNonBlocking() { } + virtual ~SinkNonBlocking() { } + + virtual T read() override; + virtual void inject(T const & value) override; + + +private: + + T _data; + rtos::Mutex _mutex; + +}; + +template +class SinkBlocking : public SinkBase +{ +public: + + SinkBlocking(); + virtual ~SinkBlocking() { } + + virtual T read() override; + virtual void inject(T const & value) override; + + +private: + + T _data; + bool _is_data_available; + rtos::Mutex _mutex; + rtos::ConditionVariable _cond_data_available; + rtos::ConditionVariable _cond_slot_available; + +}; + +/************************************************************************************** + * PUBLIC MEMBER FUNCTIONS - SinkNonBlocking + **************************************************************************************/ + +template +T SinkNonBlocking::read() +{ + _mutex.lock(); + return _data; + _mutex.unlock(); +} + +template +void SinkNonBlocking::inject(T const & value) +{ + _mutex.lock(); + _data = value; + _mutex.unlock(); +} + +/************************************************************************************** + * PUBLIC MEMBER FUNCTIONS - SinkBlocking + **************************************************************************************/ + +template +SinkBlocking::SinkBlocking() +: _is_data_available{false} +, _cond_data_available(_mutex) +, _cond_slot_available(_mutex) +{ } + +template +T SinkBlocking::read() +{ + _mutex.lock(); + while (!_is_data_available) + _cond_data_available.wait(); + T const d = _data; + _is_data_available = false; + _cond_slot_available.notify_all(); + _mutex.unlock(); + return d; +} + +template +void SinkBlocking::inject(T const & value) +{ + _mutex.lock(); + while (_is_data_available) + _cond_slot_available.wait(); + _data = value; + _is_data_available = true; + _cond_data_available.notify_all(); + _mutex.unlock(); +} + +#endif /* ARDUINO_THREADS_SINK_HPP_ */ diff --git a/src/Source.hpp b/src/Source.hpp new file mode 100644 index 0000000..0a3893c --- /dev/null +++ b/src/Source.hpp @@ -0,0 +1,73 @@ +/* + * This file is part of the Arduino_ThreadsafeIO library. + * Copyright (c) 2021 Arduino SA. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ARDUINO_THREADS_SOURCE_HPP_ +#define ARDUINO_THREADS_SOURCE_HPP_ + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include +#include + +/************************************************************************************** + * FORWARD DECLARATION + **************************************************************************************/ + +template +class SinkBase; + +/************************************************************************************** + * CLASS DECLARATION + **************************************************************************************/ + +template +class Source +{ +public: + + void connectTo(SinkBase & sink); + void write(T const & value); + +private: + std::list *> _sink_list; +}; + +/************************************************************************************** + * PUBLIC MEMBER FUNCTIONS + **************************************************************************************/ + +template +void Source::connectTo(SinkBase & sink) +{ + _sink_list.push_back(&sink); +} + +template +void Source::write(T const & value) +{ + std::for_each(std::begin(_sink_list), + std::end (_sink_list), + [value](SinkBase * sink) + { + sink->inject(value); + }); +} + +#endif /* ARDUINO_THREADS_SOURCE_HPP_ */ diff --git a/src/Wire.cpp b/src/Wire.cpp deleted file mode 100644 index aed2f05..0000000 --- a/src/Wire.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "Wire.h" -#define Wire WireReal -#define Wire1 WireReal1 -#include "../Wire/Wire.cpp" -#undef Wire -#undef Wire1 - -WireClassDispatcher Wire(WireReal); -WireClassDispatcher Wire1(WireReal1); -//WireClassDispatcher Wire(WireReal); -//WireClassDispatcher Wire1(Wire1Real); \ No newline at end of file diff --git a/src/Wire.h b/src/Wire.h deleted file mode 100644 index 2827d64..0000000 --- a/src/Wire.h +++ /dev/null @@ -1,178 +0,0 @@ -#ifndef __WIRE_WRAPPER_H__ -#define __WIRE_WRAPPER_H__ - -#include "Portenta_System.h" // just a trick to allow including the real Wire.h -#define Wire WireReal -#define Wire1 WireReal1 -#include "../Wire/Wire.h" -#undef Wire -#undef Wire1 -#include "api/RingBuffer.h" - -struct requiredClocks { - osThreadId_t id; - int clock; - RingBuffer rxBuffer; - bool transaction; -}; - -class WireClassDispatcher : public HardwareI2C { - public: - WireClassDispatcher(HardwareI2C& _wire) : wire(_wire) { - sem = new rtos::Semaphore(1); - } - void begin() { - sem->acquire(); - if (!begun) { - wire.begin(); - begun = true; - } - idClock[users].id = rtos::ThisThread::get_id(); - idClock[users].clock = currentClock; - users++; - sem->release(); - } - void begin(uint8_t) { - /*doNothing*/ - } - void onReceive(void (*)(int)) { - /*doNothing*/ - } - void onRequest(void (*)()) { - /*doNothing*/ - } - - void end() { - if (users == 0) { - wire.end(); - } else { - users--; - } - } - - void setClock(uint32_t freq) { - // must be called on a per-thread basis - if (freq != currentClock) { - wire.setClock(freq); - currentClock = freq; - *findThreadClock(rtos::ThisThread::get_id()) = currentClock; - } - } - - void beginTransmission(uint8_t address) { - //lock to caller thread until endTransmission(true) is called - sem->acquire(); - int freq = *findThreadClock(rtos::ThisThread::get_id()); - if (freq != currentClock) { - setClock(freq); - } - currentThread = rtos::ThisThread::get_id(); - wire.beginTransmission(address); - } - - uint8_t endTransmission(bool stopBit) { - uint8_t res = wire.endTransmission(stopBit); - if (stopBit) { - sem->release(); - *transactionInProgress(rtos::ThisThread::get_id()) = false; - } else { - *transactionInProgress(rtos::ThisThread::get_id()) = true; - } - return res; - } - - uint8_t endTransmission(void) { - return endTransmission(true); - } - - size_t requestFrom(uint8_t address, size_t len, bool stopBit) { - if (!*transactionInProgress(rtos::ThisThread::get_id())) { - sem->acquire(); - } - uint8_t ret = wire.requestFrom(address, len, stopBit); - if (ret > 0) { - while (wire.available()) { - findThreadRxBuffer(rtos::ThisThread::get_id()).store_char(wire.read()); - } - } - if (stopBit) { - *transactionInProgress(rtos::ThisThread::get_id()) = false; - sem->release(); - } else { - *transactionInProgress(rtos::ThisThread::get_id()) = true; - } - return ret; - } - - size_t requestFrom(uint8_t address, size_t len) { - return requestFrom(address, len, true); - } - - size_t write(uint8_t data) { - if (currentThread != rtos::ThisThread::get_id()) { - return 0; - } - return wire.write(data); - } - - size_t write(const uint8_t* data, int len) { - if (currentThread != rtos::ThisThread::get_id()) { - return 0; - } - return wire.write(data, len); - } - - int read() { - return findThreadRxBuffer(rtos::ThisThread::get_id()).read_char(); - } - - int peek() { - return findThreadRxBuffer(rtos::ThisThread::get_id()).peek(); - } - - void flush() { - } - - int available() { - return findThreadRxBuffer(rtos::ThisThread::get_id()).available(); - } - - private: - int* findThreadClock(osThreadId_t id) { - for (int i = 0; i < 10; i++) { - if (id == idClock[i].id) { - return &idClock[i].clock; - } - } - } - bool* transactionInProgress(osThreadId_t id) { - for (int i = 0; i < 10; i++) { - if (id == idClock[i].id) { - return &idClock[i].transaction; - } - } - } - RingBuffer& findThreadRxBuffer(osThreadId_t id) { - for (int i = 0; i < 10; i++) { - if (id == idClock[i].id) { - return idClock[i].rxBuffer; - } - } - } - - private: - HardwareI2C& wire; - int users = 0; - bool begun = false; - rtos::Semaphore* sem; - int currentClock = 400000; - struct requiredClocks idClock[10]; - osThreadId_t currentThread; -}; - -extern WireClassDispatcher Wire; -extern WireClassDispatcher Wire1; - -#define TwoWire WireClassDispatcher - -#endif \ No newline at end of file