From 53ee9cfb84f07b9b58c042164742dd038a82aa6b Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Tue, 3 May 2022 22:11:27 +0100 Subject: [PATCH 01/13] Initial commit for Bonsai & Arduino article --- articles/ArduinoArticle.md | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 articles/ArduinoArticle.md diff --git a/articles/ArduinoArticle.md b/articles/ArduinoArticle.md new file mode 100644 index 00000000..c2b18927 --- /dev/null +++ b/articles/ArduinoArticle.md @@ -0,0 +1,40 @@ +# Bonsai & Arduino + +Bonsai excels at processing parallel and asynchronous streams of data (e.g. cameras, audio, etc...). However, for some applications, you might be looking for a solution to "*close the loop*". In other words, not only *see* (*read*) the world but also *act* (*write*) on it. + +While there are multiple hardware options integrated in Bonsai to achieve such a goal, the [**Arduino Package**](xref:Bonsai.Arduino) is perhaps the the easiest and most affordable option. + +In this tutorial, I will cover the basics on how to set up the communication between Bonsai and Arduino, as well some examples and best practices when writing these workflows. + + +## What is the Firmata protocol + +In order to leverage the hardware capabilities of the [**Arduino board**](https://www.arduino.cc/en/Guide/Introduction) we must be able to communicate from, and to Bonsai. A bidirectional communication protocol must therefor be in place between the computer and the Arduino board. Thankfully, the Arduino community has long solved this problem through the implementation of the [`Firmata protocol`](https://www.arduino.cc/en/reference/firmata). + +The `Firmata protocol` is a generic serial communication protocol between a software application running on a host PC (in our case Bonsai), and a family of microcontrollers (in our case an Arduino, or Arduino-compatible board). + +As in most forms of communication, both parties must be able to speak the same *language*. In our case, the `Firmata protocol` is implemented at the firmware level in our microcontroller, and is, at this point, implemented in a wide swath of computer software packages, including Bonsai. +Using this protocol, the PC can abstract from the precise hardware implementation of the microcontroller, and instead send simple serial messages ([MIDI format](http://firmata.org/wiki/Protocol)) with instructions to be read and executed by the microcontroller (E.g. "What is the state of Pin1?" or "Turn on Pin13"). + +Finally, while an extensive review of the protocol is far beyond the scope of this article, if you are curious about some of the implementation details, check the references at the end of the article. + +The next sections will be dedicated to showing you how to command an Arduino with Bonsai. I will cover the basics and leave some examples and best practices when building these workflows. At the end, I will try to cover a couple of more advanced topics that might be of interest. + +## Getting started + +### Configuring Arduino + +As I mentioned before, Arduino must be loaded with a specific firmware that implements the Firmata protocol. There are several options as to which exact implementation you can use (including coding your own implementation). For the sake of simplicity, in this tutorial I will always be referring to the `StandardFirmata` implementation. + +To configure the Arduino board with Firmata: + +1. Open the Arduino IDE and [setup your board](https://www.arduino.cc/en/Guide/ArduinoUno); +2. Open the `StandardFirmata.ino` sketch (`File -> Examples -> Firmata -> StandardFirmata`); +3. Upload the sketch to your board. + +At this point your Arduino is running Firmata and, as long as you do not upload any other sketch over it, you should not have to repeat these steps. + +## Creating an Arduino object + +Once the Arduino side of things is taken care of, we move to Bonsai. + From 56a90f92ea18e72a75227b26ff0fd21b9630e474 Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Sun, 12 Jun 2022 21:52:21 +0100 Subject: [PATCH 02/13] Add Digital I/O and AnalogInput --- articles/ArduinoArticle.md | 78 ++++++++++++++++++++++++++++++++++++-- articles/toc.yml | 2 + 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/articles/ArduinoArticle.md b/articles/ArduinoArticle.md index c2b18927..e8ec06b4 100644 --- a/articles/ArduinoArticle.md +++ b/articles/ArduinoArticle.md @@ -4,8 +4,7 @@ Bonsai excels at processing parallel and asynchronous streams of data (e.g. came While there are multiple hardware options integrated in Bonsai to achieve such a goal, the [**Arduino Package**](xref:Bonsai.Arduino) is perhaps the the easiest and most affordable option. -In this tutorial, I will cover the basics on how to set up the communication between Bonsai and Arduino, as well some examples and best practices when writing these workflows. - +In this tutorial, we will cover the basics on how to set up the communication protocol between Bonsai and Arduino, as well some examples and best practices when writing these workflows. ## What is the Firmata protocol @@ -28,7 +27,7 @@ As I mentioned before, Arduino must be loaded with a specific firmware that impl To configure the Arduino board with Firmata: -1. Open the Arduino IDE and [setup your board](https://www.arduino.cc/en/Guide/ArduinoUno); +1. Open the Arduino IDE and [setup your board](https://docs.arduino.cc/software/ide-v1/tutorials/getting-started/cores/arduino-avr); 2. Open the `StandardFirmata.ino` sketch (`File -> Examples -> Firmata -> StandardFirmata`); 3. Upload the sketch to your board. @@ -38,3 +37,76 @@ At this point your Arduino is running Firmata and, as long as you do not upload Once the Arduino side of things is taken care of, we move to Bonsai. +The first thing to setup a communication between Arduino and Bonsai is to instantiate an Arduino connection using the [**`CreateArduino`**](xref:Bonsai.Arduino.CreateArduino) operator. + +This node sets: + - The communication protocol [`Baudrate`](xref:Bonsai.Arduino.CreateArduino.BaudRate). This value must match the value previously defined in the `StandardFirmata.ino` file (by default 57600 bits/second); + + - The [`Name`](xref:Bonsai.Arduino.CreateArduino.Name) to be given to the Arduino object. While the field can be left empty, it is strongly advised to enter a non-null value. This will be the name of the object we will target later, to send/receive data to/from the Arduino; + + - The [`PortName`](xref:Bonsai.Arduino.CreateArduino.PortName), which defines the COM port the Arduino is currently connected to. If the previous [`Name`](xref:Bonsai.Arduino.CreateArduino.Name) property is left empty, [`PortName`](xref:Bonsai.Arduino.CreateArduino.PortName) will be used to name the object; + + - The [`SamplingInterval`](xref:Bonsai.Arduino.CreateArduino.SamplingInterval) that determines the frequency at which analog data is sampled from the Arduino. While in theory this value can be lowered to obtain higher sampling rates (by default 19 milliseconds or ~52Hz), given hardware resource contraints, the minimum value might differ across boards. For an Arduino UNO, for instance, this value seems to cap at 10 milliseconds. + +>> ADD WORKFLOW WITH ARDUINO OBJECT HERE + +Now that we have created an object we can establish a connection with, it is finally time to get some data from Arduino. + +## Digital Input and Output + +Reading and Writing digital values from an Arduino is acomplished by instantiating a [**`DigitalOutput`**](xref:Bonsai.Arduino.DigitalOutput) and [**`DigitalInput`**](xref:Bonsai.Arduino.DigitalInput), respectively. Both nodes require two properties to be defined: + +- The [`Pin`](xref:Bonsai.Arduino.DigitalOutput.Pin) number that the user wishes to Read/Write to; + +- The [`PortName`](xref:Bonsai.Arduino.CreateArduino.PortName) of the Arduino object . As previously stated, if [`Name`](xref:Bonsai.Arduino.CreateArduino.Name) was left empty, the Arduino board can be selected by the COM port name. However, to increase the code flexibility across setups, the user is encouraged to enter a value. Under this scenario, the previously defined [`Name`](xref:Bonsai.Arduino.CreateArduino.Name) will be selectable under [`PortName`](xref:Bonsai.Arduino.CreateArduino.PortName). This way, when running the workflow in a distinct host PC, the user will simply have to change [**`CreateArduino's`**](xref:Bonsai.Arduino.CreateArduino) [`PortName`](xref:Bonsai.Arduino.CreateArduino.PortName). + + +### Digital Input + +[**`DigitalInput`**](xref:Bonsai.Arduino.DigitalInput) source outputs a ```Boolean``` value (True/False) each time the state of the defined [`Pin`](xref:Bonsai.Arduino.DigitalOutput.Pin) number changes (i.e. "Toggles"). Additionally, at start-up, the node will output the current value of the pin. + +In addition to [**`DigitalInput`**](xref:Bonsai.Arduino.DigitalInput), [**`InputPullUp`**](xref:Bonsai.Arduino.InputPullUp) is also able to report the state of a digital input pin. Using this node enables the internal pull-up resistor, affording an identical behavior to setting [```pinMode(pin, INPUT_PULLUP)```](https://www.arduino.cc/reference/en/language/functions/digital-io/pinmode/) in Arduino code. + +It should be noted that while any of the read events do not carry any temporal information from the Arduino, they can be timestamped in Bonsai with the [**`Timestamp`**](xref:Bonsai.Reactive.Timestamp) operator. Critically, the logged time will correspond to the time the event was registered in Bonsai and not when it was detected in hardware. + +>> ADD WORKFLOW WITH ARDUINO OBJECT HERE + +### Digital Output + +[**`DigitalOutput`**](xref:Bonsai.Arduino.DigitalOutput), in contrast to the previous node, instructs the Arduino to change the state of a given [`Pin`](xref:Bonsai.Arduino.DigitalOutput.Pin). This node accepts a single input in the form of a ```Boolean``` that is used to set the state of the output pin (True=HIGH, False=LOW). The new value will remain set until a distinct value is received. + +>> ADD WORKFLOW WITH ARDUINO OBJECT HERE + + +### Combining DigitalOutput and DigitalInput to measure communication latencies + +When using Arduino to control experimental rigs, especially those implementing closed-loop interactions, it is important to benchmark how long it takes for an instruction sent from Bonsai to produce an output in the world. + +One way to achieve this is to measure the time it takes to detect a change in a pin state connected to a second pin we write to. Let's first connect pin 5 to pin 6 in Arduino. We will then read from pin 5 (e.g. False) and use this value to update the state of pin 6 (i.e. NOT(False) = True). This operation will change the state of pin 5 (e.g. True) and restart a new loop. The time between each toggle read (e.g. False -> True) will give us a benchmark the round-trip time (TODO ADD WORKSHEET EXAMPLE HERE). + +>> ADD WORKFLOW WITH ARDUINO OBJECT HERE + + +## Analog Input and Output + +### Analog Input + +[**`AnalogInput`**](xref:Bonsai.Arduino.AnalogInput) can be used to read analog values from the Arduino pin. It should be noted that only [analog-read enabled pins](https://www.arduino.cc/reference/en/language/functions/analog-io/analogread/) are compatible with this functionality. For instance, in Arduino UNO, 6 analog pins are available and can be address by ```AX``` (e.g. ```A1```). + +The output of [**`AnalogInput`**](xref:Bonsai.Arduino.AnalogInput) is an ```Int``` ranging from `0-1023` (`10 bits`) that linearly maps to the digitized voltage range of the analog input pin (e.g. for Arduino UNO, `0-1023` -> `0-5 Volts`). + +Finally, the sampling rate of this node is defined by [`SamplingInterval`](xref:Bonsai.Arduino.CreateArduino.SamplingInterval). While the sampling frequency is relatively stable, a small delay (and jitter) is to be expected from the time of acquisition to receiving data in Bonsai. + + + + +[![Example Workflow](../images/acquisition-example.svg)](../workflows/acquisition-example.bonsai) +[![Example Workflow](../images/acquisition-example.svg)](../workflows/acquisition-example.bonsai) + + + +## References: + +- https://github.com/martin-eden/firmata_protocol/blob/main/protocol.md + +- http://firmata.org/wiki/Protocol diff --git a/articles/toc.yml b/articles/toc.yml index ff89ef1f..3c645255 100644 --- a/articles/toc.yml +++ b/articles/toc.yml @@ -1,2 +1,4 @@ - name: Introduction href: intro.md +- name : Arduino + href: ArduinoArticle.md \ No newline at end of file From dac8bdf8ea7cb02e0f4fbdf935700972a870b205 Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Mon, 13 Jun 2022 08:47:54 +0100 Subject: [PATCH 03/13] add analogOutput --- articles/ArduinoArticle.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/articles/ArduinoArticle.md b/articles/ArduinoArticle.md index e8ec06b4..6b754147 100644 --- a/articles/ArduinoArticle.md +++ b/articles/ArduinoArticle.md @@ -77,7 +77,6 @@ It should be noted that while any of the read events do not carry any temporal i >> ADD WORKFLOW WITH ARDUINO OBJECT HERE - ### Combining DigitalOutput and DigitalInput to measure communication latencies When using Arduino to control experimental rigs, especially those implementing closed-loop interactions, it is important to benchmark how long it takes for an instruction sent from Bonsai to produce an output in the world. @@ -97,7 +96,21 @@ The output of [**`AnalogInput`**](xref:Bonsai.Arduino.AnalogInput) is an ```Int` Finally, the sampling rate of this node is defined by [`SamplingInterval`](xref:Bonsai.Arduino.CreateArduino.SamplingInterval). While the sampling frequency is relatively stable, a small delay (and jitter) is to be expected from the time of acquisition to receiving data in Bonsai. +>> ADD WORKFLOW WITH ARDUINO OBJECT HERE + +### Analog Write + +The [**`AnalogOutput`**](xref:Bonsai.Arduino.AnalogOutput) operator implements the [`analogWrite(pin, value)`](https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/) function found in Arduino sketches. +In most boards, this function does not implement a "true" analog output, instead it tries to "approximate" an analog signal using [Pulse-Width Modulation (`PWM`)](https://en.wikipedia.org/wiki/Pulse-width_modulation). When using `PWM` in Arduino, a single input must be provided that determines the duty-cycle of the output square-wave. + +Thus, [**`AnalogOutput`**](xref:Bonsai.Arduino.AnalogOutput) receives as an input an ```Int``` in the range `0-255` `(8 bits)`. This value will linearly map to the output `PWM` duty cycle (i.e. `0%-100%`). Once a value is received, the Arduino board will continuously generate a PWM wave (by default, [in Arduino UNO, 500Hz or 1kHz](https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/)) with the specified duty-cycle. The wave can be stoped by simply sending a `0` value. + +| ![PWM_figure](https://upload.wikimedia.org/wikipedia/commons/b/b8/Duty_Cycle_Examples.png) | +|:--:| +| **Pulse-width modulation of a square wave. A 50%, 75%, and 25% duty-cycle would correspond to an [**`AnalogOutput`**](xref:Bonsai.Arduino.AnalogOutput) input value of 128, 191 and 64, respectively.** (Reproduced from: https://en.wikipedia.org/wiki/Pulse-width_modulation under a CC BY-SA 4.0 license)| + +>> ADD WORKFLOW WITH ARDUINO OBJECT HERE [![Example Workflow](../images/acquisition-example.svg)](../workflows/acquisition-example.bonsai) From c61ceef5c8eb5062ea1c3755e772abc2157c42e6 Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Tue, 14 Jun 2022 08:15:19 +0100 Subject: [PATCH 04/13] pre-rebase --- articles/ArduinoArticle.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/articles/ArduinoArticle.md b/articles/ArduinoArticle.md index 6b754147..ec22e9e7 100644 --- a/articles/ArduinoArticle.md +++ b/articles/ArduinoArticle.md @@ -117,6 +117,18 @@ Thus, [**`AnalogOutput`**](xref:Bonsai.Arduino.AnalogOutput) receives as an inpu [![Example Workflow](../images/acquisition-example.svg)](../workflows/acquisition-example.bonsai) +## Servo Output + +In addition to + +## Coding best practices + +### Subjects + + +## Alternatives to Firmata + +### An example of a simple Serial communication protocol ## References: From 27735f7bc0b176e75698ca948af88b549dd007c6 Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Tue, 3 May 2022 22:11:27 +0100 Subject: [PATCH 05/13] Initial commit for Bonsai & Arduino article --- articles/ArduinoArticle.md | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 articles/ArduinoArticle.md diff --git a/articles/ArduinoArticle.md b/articles/ArduinoArticle.md new file mode 100644 index 00000000..c2b18927 --- /dev/null +++ b/articles/ArduinoArticle.md @@ -0,0 +1,40 @@ +# Bonsai & Arduino + +Bonsai excels at processing parallel and asynchronous streams of data (e.g. cameras, audio, etc...). However, for some applications, you might be looking for a solution to "*close the loop*". In other words, not only *see* (*read*) the world but also *act* (*write*) on it. + +While there are multiple hardware options integrated in Bonsai to achieve such a goal, the [**Arduino Package**](xref:Bonsai.Arduino) is perhaps the the easiest and most affordable option. + +In this tutorial, I will cover the basics on how to set up the communication between Bonsai and Arduino, as well some examples and best practices when writing these workflows. + + +## What is the Firmata protocol + +In order to leverage the hardware capabilities of the [**Arduino board**](https://www.arduino.cc/en/Guide/Introduction) we must be able to communicate from, and to Bonsai. A bidirectional communication protocol must therefor be in place between the computer and the Arduino board. Thankfully, the Arduino community has long solved this problem through the implementation of the [`Firmata protocol`](https://www.arduino.cc/en/reference/firmata). + +The `Firmata protocol` is a generic serial communication protocol between a software application running on a host PC (in our case Bonsai), and a family of microcontrollers (in our case an Arduino, or Arduino-compatible board). + +As in most forms of communication, both parties must be able to speak the same *language*. In our case, the `Firmata protocol` is implemented at the firmware level in our microcontroller, and is, at this point, implemented in a wide swath of computer software packages, including Bonsai. +Using this protocol, the PC can abstract from the precise hardware implementation of the microcontroller, and instead send simple serial messages ([MIDI format](http://firmata.org/wiki/Protocol)) with instructions to be read and executed by the microcontroller (E.g. "What is the state of Pin1?" or "Turn on Pin13"). + +Finally, while an extensive review of the protocol is far beyond the scope of this article, if you are curious about some of the implementation details, check the references at the end of the article. + +The next sections will be dedicated to showing you how to command an Arduino with Bonsai. I will cover the basics and leave some examples and best practices when building these workflows. At the end, I will try to cover a couple of more advanced topics that might be of interest. + +## Getting started + +### Configuring Arduino + +As I mentioned before, Arduino must be loaded with a specific firmware that implements the Firmata protocol. There are several options as to which exact implementation you can use (including coding your own implementation). For the sake of simplicity, in this tutorial I will always be referring to the `StandardFirmata` implementation. + +To configure the Arduino board with Firmata: + +1. Open the Arduino IDE and [setup your board](https://www.arduino.cc/en/Guide/ArduinoUno); +2. Open the `StandardFirmata.ino` sketch (`File -> Examples -> Firmata -> StandardFirmata`); +3. Upload the sketch to your board. + +At this point your Arduino is running Firmata and, as long as you do not upload any other sketch over it, you should not have to repeat these steps. + +## Creating an Arduino object + +Once the Arduino side of things is taken care of, we move to Bonsai. + From b395259e62df0ffb8153d03a6236b207049715df Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Sun, 12 Jun 2022 21:52:21 +0100 Subject: [PATCH 06/13] Add Digital I/O and AnalogInput --- articles/ArduinoArticle.md | 78 ++++++++++++++++++++++++++++++++++++-- articles/toc.yml | 2 + 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/articles/ArduinoArticle.md b/articles/ArduinoArticle.md index c2b18927..e8ec06b4 100644 --- a/articles/ArduinoArticle.md +++ b/articles/ArduinoArticle.md @@ -4,8 +4,7 @@ Bonsai excels at processing parallel and asynchronous streams of data (e.g. came While there are multiple hardware options integrated in Bonsai to achieve such a goal, the [**Arduino Package**](xref:Bonsai.Arduino) is perhaps the the easiest and most affordable option. -In this tutorial, I will cover the basics on how to set up the communication between Bonsai and Arduino, as well some examples and best practices when writing these workflows. - +In this tutorial, we will cover the basics on how to set up the communication protocol between Bonsai and Arduino, as well some examples and best practices when writing these workflows. ## What is the Firmata protocol @@ -28,7 +27,7 @@ As I mentioned before, Arduino must be loaded with a specific firmware that impl To configure the Arduino board with Firmata: -1. Open the Arduino IDE and [setup your board](https://www.arduino.cc/en/Guide/ArduinoUno); +1. Open the Arduino IDE and [setup your board](https://docs.arduino.cc/software/ide-v1/tutorials/getting-started/cores/arduino-avr); 2. Open the `StandardFirmata.ino` sketch (`File -> Examples -> Firmata -> StandardFirmata`); 3. Upload the sketch to your board. @@ -38,3 +37,76 @@ At this point your Arduino is running Firmata and, as long as you do not upload Once the Arduino side of things is taken care of, we move to Bonsai. +The first thing to setup a communication between Arduino and Bonsai is to instantiate an Arduino connection using the [**`CreateArduino`**](xref:Bonsai.Arduino.CreateArduino) operator. + +This node sets: + - The communication protocol [`Baudrate`](xref:Bonsai.Arduino.CreateArduino.BaudRate). This value must match the value previously defined in the `StandardFirmata.ino` file (by default 57600 bits/second); + + - The [`Name`](xref:Bonsai.Arduino.CreateArduino.Name) to be given to the Arduino object. While the field can be left empty, it is strongly advised to enter a non-null value. This will be the name of the object we will target later, to send/receive data to/from the Arduino; + + - The [`PortName`](xref:Bonsai.Arduino.CreateArduino.PortName), which defines the COM port the Arduino is currently connected to. If the previous [`Name`](xref:Bonsai.Arduino.CreateArduino.Name) property is left empty, [`PortName`](xref:Bonsai.Arduino.CreateArduino.PortName) will be used to name the object; + + - The [`SamplingInterval`](xref:Bonsai.Arduino.CreateArduino.SamplingInterval) that determines the frequency at which analog data is sampled from the Arduino. While in theory this value can be lowered to obtain higher sampling rates (by default 19 milliseconds or ~52Hz), given hardware resource contraints, the minimum value might differ across boards. For an Arduino UNO, for instance, this value seems to cap at 10 milliseconds. + +>> ADD WORKFLOW WITH ARDUINO OBJECT HERE + +Now that we have created an object we can establish a connection with, it is finally time to get some data from Arduino. + +## Digital Input and Output + +Reading and Writing digital values from an Arduino is acomplished by instantiating a [**`DigitalOutput`**](xref:Bonsai.Arduino.DigitalOutput) and [**`DigitalInput`**](xref:Bonsai.Arduino.DigitalInput), respectively. Both nodes require two properties to be defined: + +- The [`Pin`](xref:Bonsai.Arduino.DigitalOutput.Pin) number that the user wishes to Read/Write to; + +- The [`PortName`](xref:Bonsai.Arduino.CreateArduino.PortName) of the Arduino object . As previously stated, if [`Name`](xref:Bonsai.Arduino.CreateArduino.Name) was left empty, the Arduino board can be selected by the COM port name. However, to increase the code flexibility across setups, the user is encouraged to enter a value. Under this scenario, the previously defined [`Name`](xref:Bonsai.Arduino.CreateArduino.Name) will be selectable under [`PortName`](xref:Bonsai.Arduino.CreateArduino.PortName). This way, when running the workflow in a distinct host PC, the user will simply have to change [**`CreateArduino's`**](xref:Bonsai.Arduino.CreateArduino) [`PortName`](xref:Bonsai.Arduino.CreateArduino.PortName). + + +### Digital Input + +[**`DigitalInput`**](xref:Bonsai.Arduino.DigitalInput) source outputs a ```Boolean``` value (True/False) each time the state of the defined [`Pin`](xref:Bonsai.Arduino.DigitalOutput.Pin) number changes (i.e. "Toggles"). Additionally, at start-up, the node will output the current value of the pin. + +In addition to [**`DigitalInput`**](xref:Bonsai.Arduino.DigitalInput), [**`InputPullUp`**](xref:Bonsai.Arduino.InputPullUp) is also able to report the state of a digital input pin. Using this node enables the internal pull-up resistor, affording an identical behavior to setting [```pinMode(pin, INPUT_PULLUP)```](https://www.arduino.cc/reference/en/language/functions/digital-io/pinmode/) in Arduino code. + +It should be noted that while any of the read events do not carry any temporal information from the Arduino, they can be timestamped in Bonsai with the [**`Timestamp`**](xref:Bonsai.Reactive.Timestamp) operator. Critically, the logged time will correspond to the time the event was registered in Bonsai and not when it was detected in hardware. + +>> ADD WORKFLOW WITH ARDUINO OBJECT HERE + +### Digital Output + +[**`DigitalOutput`**](xref:Bonsai.Arduino.DigitalOutput), in contrast to the previous node, instructs the Arduino to change the state of a given [`Pin`](xref:Bonsai.Arduino.DigitalOutput.Pin). This node accepts a single input in the form of a ```Boolean``` that is used to set the state of the output pin (True=HIGH, False=LOW). The new value will remain set until a distinct value is received. + +>> ADD WORKFLOW WITH ARDUINO OBJECT HERE + + +### Combining DigitalOutput and DigitalInput to measure communication latencies + +When using Arduino to control experimental rigs, especially those implementing closed-loop interactions, it is important to benchmark how long it takes for an instruction sent from Bonsai to produce an output in the world. + +One way to achieve this is to measure the time it takes to detect a change in a pin state connected to a second pin we write to. Let's first connect pin 5 to pin 6 in Arduino. We will then read from pin 5 (e.g. False) and use this value to update the state of pin 6 (i.e. NOT(False) = True). This operation will change the state of pin 5 (e.g. True) and restart a new loop. The time between each toggle read (e.g. False -> True) will give us a benchmark the round-trip time (TODO ADD WORKSHEET EXAMPLE HERE). + +>> ADD WORKFLOW WITH ARDUINO OBJECT HERE + + +## Analog Input and Output + +### Analog Input + +[**`AnalogInput`**](xref:Bonsai.Arduino.AnalogInput) can be used to read analog values from the Arduino pin. It should be noted that only [analog-read enabled pins](https://www.arduino.cc/reference/en/language/functions/analog-io/analogread/) are compatible with this functionality. For instance, in Arduino UNO, 6 analog pins are available and can be address by ```AX``` (e.g. ```A1```). + +The output of [**`AnalogInput`**](xref:Bonsai.Arduino.AnalogInput) is an ```Int``` ranging from `0-1023` (`10 bits`) that linearly maps to the digitized voltage range of the analog input pin (e.g. for Arduino UNO, `0-1023` -> `0-5 Volts`). + +Finally, the sampling rate of this node is defined by [`SamplingInterval`](xref:Bonsai.Arduino.CreateArduino.SamplingInterval). While the sampling frequency is relatively stable, a small delay (and jitter) is to be expected from the time of acquisition to receiving data in Bonsai. + + + + +[![Example Workflow](../images/acquisition-example.svg)](../workflows/acquisition-example.bonsai) +[![Example Workflow](../images/acquisition-example.svg)](../workflows/acquisition-example.bonsai) + + + +## References: + +- https://github.com/martin-eden/firmata_protocol/blob/main/protocol.md + +- http://firmata.org/wiki/Protocol diff --git a/articles/toc.yml b/articles/toc.yml index ff89ef1f..3c645255 100644 --- a/articles/toc.yml +++ b/articles/toc.yml @@ -1,2 +1,4 @@ - name: Introduction href: intro.md +- name : Arduino + href: ArduinoArticle.md \ No newline at end of file From d13a814220e6551e50c78949df46527c97bc65ec Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Mon, 13 Jun 2022 08:47:54 +0100 Subject: [PATCH 07/13] add analogOutput --- articles/ArduinoArticle.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/articles/ArduinoArticle.md b/articles/ArduinoArticle.md index e8ec06b4..6b754147 100644 --- a/articles/ArduinoArticle.md +++ b/articles/ArduinoArticle.md @@ -77,7 +77,6 @@ It should be noted that while any of the read events do not carry any temporal i >> ADD WORKFLOW WITH ARDUINO OBJECT HERE - ### Combining DigitalOutput and DigitalInput to measure communication latencies When using Arduino to control experimental rigs, especially those implementing closed-loop interactions, it is important to benchmark how long it takes for an instruction sent from Bonsai to produce an output in the world. @@ -97,7 +96,21 @@ The output of [**`AnalogInput`**](xref:Bonsai.Arduino.AnalogInput) is an ```Int` Finally, the sampling rate of this node is defined by [`SamplingInterval`](xref:Bonsai.Arduino.CreateArduino.SamplingInterval). While the sampling frequency is relatively stable, a small delay (and jitter) is to be expected from the time of acquisition to receiving data in Bonsai. +>> ADD WORKFLOW WITH ARDUINO OBJECT HERE + +### Analog Write + +The [**`AnalogOutput`**](xref:Bonsai.Arduino.AnalogOutput) operator implements the [`analogWrite(pin, value)`](https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/) function found in Arduino sketches. +In most boards, this function does not implement a "true" analog output, instead it tries to "approximate" an analog signal using [Pulse-Width Modulation (`PWM`)](https://en.wikipedia.org/wiki/Pulse-width_modulation). When using `PWM` in Arduino, a single input must be provided that determines the duty-cycle of the output square-wave. + +Thus, [**`AnalogOutput`**](xref:Bonsai.Arduino.AnalogOutput) receives as an input an ```Int``` in the range `0-255` `(8 bits)`. This value will linearly map to the output `PWM` duty cycle (i.e. `0%-100%`). Once a value is received, the Arduino board will continuously generate a PWM wave (by default, [in Arduino UNO, 500Hz or 1kHz](https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/)) with the specified duty-cycle. The wave can be stoped by simply sending a `0` value. + +| ![PWM_figure](https://upload.wikimedia.org/wikipedia/commons/b/b8/Duty_Cycle_Examples.png) | +|:--:| +| **Pulse-width modulation of a square wave. A 50%, 75%, and 25% duty-cycle would correspond to an [**`AnalogOutput`**](xref:Bonsai.Arduino.AnalogOutput) input value of 128, 191 and 64, respectively.** (Reproduced from: https://en.wikipedia.org/wiki/Pulse-width_modulation under a CC BY-SA 4.0 license)| + +>> ADD WORKFLOW WITH ARDUINO OBJECT HERE [![Example Workflow](../images/acquisition-example.svg)](../workflows/acquisition-example.bonsai) From 01e7c3323f1649394e3e25c7051d3c65ae63778a Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Tue, 14 Jun 2022 08:15:19 +0100 Subject: [PATCH 08/13] pre-rebase --- articles/ArduinoArticle.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/articles/ArduinoArticle.md b/articles/ArduinoArticle.md index 6b754147..ec22e9e7 100644 --- a/articles/ArduinoArticle.md +++ b/articles/ArduinoArticle.md @@ -117,6 +117,18 @@ Thus, [**`AnalogOutput`**](xref:Bonsai.Arduino.AnalogOutput) receives as an inpu [![Example Workflow](../images/acquisition-example.svg)](../workflows/acquisition-example.bonsai) +## Servo Output + +In addition to + +## Coding best practices + +### Subjects + + +## Alternatives to Firmata + +### An example of a simple Serial communication protocol ## References: From e40bc145d475211f491239d0c4156c8ed5ac019d Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Tue, 14 Jun 2022 08:27:10 +0100 Subject: [PATCH 09/13] Fix broken links --- articles/ArduinoArticle.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/articles/ArduinoArticle.md b/articles/ArduinoArticle.md index ec22e9e7..3e7f721e 100644 --- a/articles/ArduinoArticle.md +++ b/articles/ArduinoArticle.md @@ -6,9 +6,9 @@ While there are multiple hardware options integrated in Bonsai to achieve such a In this tutorial, we will cover the basics on how to set up the communication protocol between Bonsai and Arduino, as well some examples and best practices when writing these workflows. -## What is the Firmata protocol +## The Firmata protocol -In order to leverage the hardware capabilities of the [**Arduino board**](https://www.arduino.cc/en/Guide/Introduction) we must be able to communicate from, and to Bonsai. A bidirectional communication protocol must therefor be in place between the computer and the Arduino board. Thankfully, the Arduino community has long solved this problem through the implementation of the [`Firmata protocol`](https://www.arduino.cc/en/reference/firmata). +In order to leverage the hardware capabilities of the [**Arduino board**](https://www.arduino.cc/en/Guide/Introduction) we must be able to communicate from, and to Bonsai. A bidirectional communication protocol must therefor be in place between the computer and the Arduino board. Thankfully, the Arduino community has long solved this problem through the implementation of the [`Firmata protocol`](https://www.arduino.cc/reference/en/libraries/firmata/). The `Firmata protocol` is a generic serial communication protocol between a software application running on a host PC (in our case Bonsai), and a family of microcontrollers (in our case an Arduino, or Arduino-compatible board). @@ -73,7 +73,7 @@ It should be noted that while any of the read events do not carry any temporal i ### Digital Output -[**`DigitalOutput`**](xref:Bonsai.Arduino.DigitalOutput), in contrast to the previous node, instructs the Arduino to change the state of a given [`Pin`](xref:Bonsai.Arduino.DigitalOutput.Pin). This node accepts a single input in the form of a ```Boolean``` that is used to set the state of the output pin (True=HIGH, False=LOW). The new value will remain set until a distinct value is received. +[**`DigitalOutput`**](xref:Bonsai.Arduino.DigitalOutput), in contrast to the previous node, instructs the Arduino to change the state of a given [`Pin`](xref:Bonsai.Arduino.DigitalOutput.Pin). This node accepts a single input in the form of a ```Boolean``` that is used to set the state of the output pin (```True=HIGH```, ```False=LOW```). The new value will remain set until a distinct value is received. >> ADD WORKFLOW WITH ARDUINO OBJECT HERE @@ -130,7 +130,9 @@ In addition to ### An example of a simple Serial communication protocol -## References: +## General references: + +- https://github.com/firmata/arduino - https://github.com/martin-eden/firmata_protocol/blob/main/protocol.md From 398b43e8fd202303ea22600468d7cc0f0dcfddd9 Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Tue, 21 Jun 2022 08:39:54 +0100 Subject: [PATCH 10/13] Add header --- articles/ArduinoArticle.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/articles/ArduinoArticle.md b/articles/ArduinoArticle.md index 3e7f721e..300e3716 100644 --- a/articles/ArduinoArticle.md +++ b/articles/ArduinoArticle.md @@ -1,3 +1,7 @@ +--- +layout: article +title: Bonsai & Arduino +--- # Bonsai & Arduino Bonsai excels at processing parallel and asynchronous streams of data (e.g. cameras, audio, etc...). However, for some applications, you might be looking for a solution to "*close the loop*". In other words, not only *see* (*read*) the world but also *act* (*write*) on it. From 22e29663579c00daa2054b4805e9d335c0584751 Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Sun, 3 Jul 2022 11:13:39 +0100 Subject: [PATCH 11/13] Add ServoOutput and Subjects (WIP) --- articles/ArduinoArticle.md | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/articles/ArduinoArticle.md b/articles/ArduinoArticle.md index 300e3716..74777436 100644 --- a/articles/ArduinoArticle.md +++ b/articles/ArduinoArticle.md @@ -123,12 +123,41 @@ Thus, [**`AnalogOutput`**](xref:Bonsai.Arduino.AnalogOutput) receives as an inpu ## Servo Output -In addition to +In addition to writing analog and digital values, the communication between Bonsai and Arduino is also able to control servo motors using, under the hood, the ['Servo.h'](https://www.arduino.cc/reference/en/libraries/servo/) library. +Similarly to the previously showcased outputs, the [**`ServoOutput`**](xref:Bonsai.Arduino.ServoOutput) operator expects a [`Pin`](xref:Bonsai.Arduino.ServoOutput.Pin) connected to the servo-motor, and a [`PortName`](xref:Bonsai.Arduino.ServoOutput.PorName). You can instruct the servo to move to a specific angle (`0-180 degrees`) by simply sending an ```Int``` input to the operator. + +[![Example Workflow](../images/CreateArduino.svg)](../workflows/CreateArduino.bonsai) ## Coding best practices +We have now covered the main operators that afford communication between Bonsai and Arduino. In this next section we will review coding pratices that will make your workflow more readable, scalable, and sometimes performant. ### Subjects +Covering the topic of `Subjects` is far from the scope of this tutorial. For now, let's just take the intuition that `Subjects` are operators that allow the sharing of observables across your workflow, without the need for explicit branches to be made between operators. +For instance: + +The output of timer is shared using a [**`PublishSubject`**](xref:Bonsai.Expressions.PublishSubject), and subscribed to using [**`SubscribeSubject`**](xref:Bonsai.Expressions.SubscribeSubject) from anywhere in your workflow. Keep in mind that, in order to pair connections between `Subjects`, these must have names. This can also be leveraged to keep your code cleaner as we will see. + +As a side node, due to the priority `Subjects` are initialized with in Bonsai, it is highly recomended to use them to define all your hardware objects and connections at the highest level of your workflow. + + +#### Subjects afford "one-to-many" logic +Consider the example wherein you might be interested in reading from a single digital pin and perform two independent computations in Bonsai. This would look something like: + +--input(1) -go1 +--input(1) -go2 + +While valid, this creates a problem if you need to change the pin you want to read from. Since the number of changes you will need to refactor in your workflow will scale with the number of branches that use that operator. + +A good pratice to avoid these pitfals is to assign your observable to a subject with a more abstract name (e.g. `ButtonSignal`). + +--workflow + +As you can probably tell, as long as the downstream branches are subscribed to `ButtonSignal`, you would simply need to change the pin number in a single place (i.e. when creating the `Subject`) + +#### Subjects afford One-to-many logic "many-to-one" logic + + ## Alternatives to Firmata From 34c69c7048178d17292c58e3d799a2c4927a237c Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Thu, 7 Jul 2022 11:17:23 +0100 Subject: [PATCH 12/13] Add workflow examples --- articles/ArduinoArticle.md | 54 ++++---- workflows/ArduinoAnalogIO.bonsai | 38 ++++++ workflows/ArduinoAnalogIO.svg | 3 + workflows/ArduinoDigitalIO.bonsai | 38 ++++++ workflows/ArduinoDigitalIO.svg | 3 + workflows/ArduinoOneToMany.bonsai | 107 ++++++++++++++++ workflows/ArduinoOneToMany.svg | 3 + workflows/ArduinoOneToManyRefactored.bonsai | 111 +++++++++++++++++ workflows/ArduinoOneToManyRefactored.svg | 3 + ...duinoOneToManyRefactored_withanalog.bonsai | 117 ++++++++++++++++++ .../ArduinoOneToManyRefactored_withanalog.svg | 3 + workflows/ArduinoRoundTrip.bonsai | 44 +++++++ workflows/ArduinoRoundTrip.svg | 3 + workflows/ArduinoServo.bonsai | 32 +++++ workflows/ArduinoServo.svg | 3 + 15 files changed, 536 insertions(+), 26 deletions(-) create mode 100644 workflows/ArduinoAnalogIO.bonsai create mode 100644 workflows/ArduinoAnalogIO.svg create mode 100644 workflows/ArduinoDigitalIO.bonsai create mode 100644 workflows/ArduinoDigitalIO.svg create mode 100644 workflows/ArduinoOneToMany.bonsai create mode 100644 workflows/ArduinoOneToMany.svg create mode 100644 workflows/ArduinoOneToManyRefactored.bonsai create mode 100644 workflows/ArduinoOneToManyRefactored.svg create mode 100644 workflows/ArduinoOneToManyRefactored_withanalog.bonsai create mode 100644 workflows/ArduinoOneToManyRefactored_withanalog.svg create mode 100644 workflows/ArduinoRoundTrip.bonsai create mode 100644 workflows/ArduinoRoundTrip.svg create mode 100644 workflows/ArduinoServo.bonsai create mode 100644 workflows/ArduinoServo.svg diff --git a/articles/ArduinoArticle.md b/articles/ArduinoArticle.md index 74777436..7aaa1027 100644 --- a/articles/ArduinoArticle.md +++ b/articles/ArduinoArticle.md @@ -52,8 +52,6 @@ This node sets: - The [`SamplingInterval`](xref:Bonsai.Arduino.CreateArduino.SamplingInterval) that determines the frequency at which analog data is sampled from the Arduino. While in theory this value can be lowered to obtain higher sampling rates (by default 19 milliseconds or ~52Hz), given hardware resource contraints, the minimum value might differ across boards. For an Arduino UNO, for instance, this value seems to cap at 10 milliseconds. ->> ADD WORKFLOW WITH ARDUINO OBJECT HERE - Now that we have created an object we can establish a connection with, it is finally time to get some data from Arduino. ## Digital Input and Output @@ -64,7 +62,6 @@ Reading and Writing digital values from an Arduino is acomplished by instantiati - The [`PortName`](xref:Bonsai.Arduino.CreateArduino.PortName) of the Arduino object . As previously stated, if [`Name`](xref:Bonsai.Arduino.CreateArduino.Name) was left empty, the Arduino board can be selected by the COM port name. However, to increase the code flexibility across setups, the user is encouraged to enter a value. Under this scenario, the previously defined [`Name`](xref:Bonsai.Arduino.CreateArduino.Name) will be selectable under [`PortName`](xref:Bonsai.Arduino.CreateArduino.PortName). This way, when running the workflow in a distinct host PC, the user will simply have to change [**`CreateArduino's`**](xref:Bonsai.Arduino.CreateArduino) [`PortName`](xref:Bonsai.Arduino.CreateArduino.PortName). - ### Digital Input [**`DigitalInput`**](xref:Bonsai.Arduino.DigitalInput) source outputs a ```Boolean``` value (True/False) each time the state of the defined [`Pin`](xref:Bonsai.Arduino.DigitalOutput.Pin) number changes (i.e. "Toggles"). Additionally, at start-up, the node will output the current value of the pin. @@ -73,22 +70,23 @@ In addition to [**`DigitalInput`**](xref:Bonsai.Arduino.DigitalInput), [**`Input It should be noted that while any of the read events do not carry any temporal information from the Arduino, they can be timestamped in Bonsai with the [**`Timestamp`**](xref:Bonsai.Reactive.Timestamp) operator. Critically, the logged time will correspond to the time the event was registered in Bonsai and not when it was detected in hardware. ->> ADD WORKFLOW WITH ARDUINO OBJECT HERE - ### Digital Output [**`DigitalOutput`**](xref:Bonsai.Arduino.DigitalOutput), in contrast to the previous node, instructs the Arduino to change the state of a given [`Pin`](xref:Bonsai.Arduino.DigitalOutput.Pin). This node accepts a single input in the form of a ```Boolean``` that is used to set the state of the output pin (```True=HIGH```, ```False=LOW```). The new value will remain set until a distinct value is received. ->> ADD WORKFLOW WITH ARDUINO OBJECT HERE +:::workflow +![Arduino Digital I/O](~/workflows/ArduinoDigitalIO.bonsai) +::: -### Combining DigitalOutput and DigitalInput to measure communication latencies +### Measuring communication latency When using Arduino to control experimental rigs, especially those implementing closed-loop interactions, it is important to benchmark how long it takes for an instruction sent from Bonsai to produce an output in the world. -One way to achieve this is to measure the time it takes to detect a change in a pin state connected to a second pin we write to. Let's first connect pin 5 to pin 6 in Arduino. We will then read from pin 5 (e.g. False) and use this value to update the state of pin 6 (i.e. NOT(False) = True). This operation will change the state of pin 5 (e.g. True) and restart a new loop. The time between each toggle read (e.g. False -> True) will give us a benchmark the round-trip time (TODO ADD WORKSHEET EXAMPLE HERE). - ->> ADD WORKFLOW WITH ARDUINO OBJECT HERE +One way to achieve this is to measure the time it takes to detect a change in a pin state connected to a second pin we write to. Let's first connect a wire from pin 5 to pin 6 in Arduino. We will then read from pin 5 (e.g. False) and use this value to update the state of pin 6 (i.e. NOT(False) = True). This operation will change the state of pin 5 (e.g. True) and restart a new loop. The time between each toggle read (e.g. False -> True) will give us a benchmark the round-trip time. +:::workflow +![Measuring round-trip delay](~/workflows/ArduinoRoundTrip.bonsai) +::: ## Analog Input and Output @@ -100,8 +98,6 @@ The output of [**`AnalogInput`**](xref:Bonsai.Arduino.AnalogInput) is an ```Int` Finally, the sampling rate of this node is defined by [`SamplingInterval`](xref:Bonsai.Arduino.CreateArduino.SamplingInterval). While the sampling frequency is relatively stable, a small delay (and jitter) is to be expected from the time of acquisition to receiving data in Bonsai. ->> ADD WORKFLOW WITH ARDUINO OBJECT HERE - ### Analog Write The [**`AnalogOutput`**](xref:Bonsai.Arduino.AnalogOutput) operator implements the [`analogWrite(pin, value)`](https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/) function found in Arduino sketches. @@ -114,21 +110,20 @@ Thus, [**`AnalogOutput`**](xref:Bonsai.Arduino.AnalogOutput) receives as an inpu |:--:| | **Pulse-width modulation of a square wave. A 50%, 75%, and 25% duty-cycle would correspond to an [**`AnalogOutput`**](xref:Bonsai.Arduino.AnalogOutput) input value of 128, 191 and 64, respectively.** (Reproduced from: https://en.wikipedia.org/wiki/Pulse-width_modulation under a CC BY-SA 4.0 license)| ->> ADD WORKFLOW WITH ARDUINO OBJECT HERE - - -[![Example Workflow](../images/acquisition-example.svg)](../workflows/acquisition-example.bonsai) -[![Example Workflow](../images/acquisition-example.svg)](../workflows/acquisition-example.bonsai) - +:::workflow +![Arduino Analog I/O](~/workflows/ArduinoAnalogIO.bonsai) +::: ## Servo Output In addition to writing analog and digital values, the communication between Bonsai and Arduino is also able to control servo motors using, under the hood, the ['Servo.h'](https://www.arduino.cc/reference/en/libraries/servo/) library. Similarly to the previously showcased outputs, the [**`ServoOutput`**](xref:Bonsai.Arduino.ServoOutput) operator expects a [`Pin`](xref:Bonsai.Arduino.ServoOutput.Pin) connected to the servo-motor, and a [`PortName`](xref:Bonsai.Arduino.ServoOutput.PorName). You can instruct the servo to move to a specific angle (`0-180 degrees`) by simply sending an ```Int``` input to the operator. -[![Example Workflow](../images/CreateArduino.svg)](../workflows/CreateArduino.bonsai) +:::workflow +![Arduino Servo Output](~/workflows/ArduinoServo.bonsai) +::: -## Coding best practices +## Best practices We have now covered the main operators that afford communication between Bonsai and Arduino. In this next section we will review coding pratices that will make your workflow more readable, scalable, and sometimes performant. ### Subjects @@ -144,19 +139,26 @@ As a side node, due to the priority `Subjects` are initialized with in Bonsai, i #### Subjects afford "one-to-many" logic Consider the example wherein you might be interested in reading from a single digital pin and perform two independent computations in Bonsai. This would look something like: ---input(1) -go1 ---input(1) -go2 +:::workflow +![One-to-many](~/workflows/ArduinoOneToMany.bonsai) +::: + While valid, this creates a problem if you need to change the pin you want to read from. Since the number of changes you will need to refactor in your workflow will scale with the number of branches that use that operator. -A good pratice to avoid these pitfals is to assign your observable to a subject with a more abstract name (e.g. `ButtonSignal`). +A good pratice to avoid these pitfals is to assign your observable to a subject with a more abstract name (e.g. `MySignal`). ---workflow +:::workflow +![One-to-many refactored](~/workflows/ArduinoOneToManyRefactored.bonsai) +::: -As you can probably tell, as long as the downstream branches are subscribed to `ButtonSignal`, you would simply need to change the pin number in a single place (i.e. when creating the `Subject`) +As you can probably tell, as long as the downstream branches are subscribed to `MySignal`, you would simply need to change the pin number in a single place (i.e. when creating the `MySignal` `Subject`). Additionally, this solution also affords the possiblity of completly changing the computation prior to `MySignal`. For instance, instead of using a digital input pin, we could opt to read from an analog signal and threshold it software. -#### Subjects afford One-to-many logic "many-to-one" logic +:::workflow +![One-to-many refactored with analog input](~/workflows/ArduinoOneToManyRefactored_withanalog.bonsai) +::: +#### Subjects afford One-to-many logic "many-to-one" logic ## Alternatives to Firmata diff --git a/workflows/ArduinoAnalogIO.bonsai b/workflows/ArduinoAnalogIO.bonsai new file mode 100644 index 00000000..00e9b520 --- /dev/null +++ b/workflows/ArduinoAnalogIO.bonsai @@ -0,0 +1,38 @@ + + + + + + + MyArduino + COM3 + 57600 + 19 + + + + + 128 + + + + + MyArduino + 11 + + + + + MyArduino + 1 + + + + + + + + \ No newline at end of file diff --git a/workflows/ArduinoAnalogIO.svg b/workflows/ArduinoAnalogIO.svg new file mode 100644 index 00000000..aa96b5cb --- /dev/null +++ b/workflows/ArduinoAnalogIO.svg @@ -0,0 +1,3 @@ + +]>MyArduinoAnalogOutputAnalogInputInt \ No newline at end of file diff --git a/workflows/ArduinoDigitalIO.bonsai b/workflows/ArduinoDigitalIO.bonsai new file mode 100644 index 00000000..06847ce5 --- /dev/null +++ b/workflows/ArduinoDigitalIO.bonsai @@ -0,0 +1,38 @@ + + + + + + + MyArduino + COM3 + 57600 + 19 + + + + + false + + + + + MyArduino + 6 + + + + + MyArduino + 7 + + + + + + + + \ No newline at end of file diff --git a/workflows/ArduinoDigitalIO.svg b/workflows/ArduinoDigitalIO.svg new file mode 100644 index 00000000..0b131119 --- /dev/null +++ b/workflows/ArduinoDigitalIO.svg @@ -0,0 +1,3 @@ + +]>MyArduinoDigitalOutputDigitalInputBoolean \ No newline at end of file diff --git a/workflows/ArduinoOneToMany.bonsai b/workflows/ArduinoOneToMany.bonsai new file mode 100644 index 00000000..47fb7cd8 --- /dev/null +++ b/workflows/ArduinoOneToMany.bonsai @@ -0,0 +1,107 @@ + + + + + + + MyArduino + COM3 + 57600 + 19 + + + + + MyArduino + 6 + + + + Output1 + + + + Source1 + + + isON? + + + + Source1 + + + + + + + + + + + ON + + + + + + + + + + + + + + MyArduino + 6 + + + + Output2 + + + + + Source1 + + + isOFF? + + + + Source1 + + + + + + + + + + + + + OFF + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/workflows/ArduinoOneToMany.svg b/workflows/ArduinoOneToMany.svg new file mode 100644 index 00000000..f9593b2e --- /dev/null +++ b/workflows/ArduinoOneToMany.svg @@ -0,0 +1,3 @@ + +]>MyArduinoOutput1Output2DigitalInputDigitalInput \ No newline at end of file diff --git a/workflows/ArduinoOneToManyRefactored.bonsai b/workflows/ArduinoOneToManyRefactored.bonsai new file mode 100644 index 00000000..e06319b2 --- /dev/null +++ b/workflows/ArduinoOneToManyRefactored.bonsai @@ -0,0 +1,111 @@ + + + + + + + MyArduino + COM3 + 57600 + 19 + + + + + MyArduino + 6 + + + + MySignal + + + MySignal + + + Output1 + + + + Source1 + + + isON? + + + + Source1 + + + + + + + + + + + ON + + + + + + + + + + + + + MySignal + + + Output2 + + + + + Source1 + + + isOFF? + + + + Source1 + + + + + + + + + + + + + OFF + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/workflows/ArduinoOneToManyRefactored.svg b/workflows/ArduinoOneToManyRefactored.svg new file mode 100644 index 00000000..bb011e60 --- /dev/null +++ b/workflows/ArduinoOneToManyRefactored.svg @@ -0,0 +1,3 @@ + +]>MyArduinoMySignalOutput1Output2DigitalInputMySignalMySignal \ No newline at end of file diff --git a/workflows/ArduinoOneToManyRefactored_withanalog.bonsai b/workflows/ArduinoOneToManyRefactored_withanalog.bonsai new file mode 100644 index 00000000..6ac6cf61 --- /dev/null +++ b/workflows/ArduinoOneToManyRefactored_withanalog.bonsai @@ -0,0 +1,117 @@ + + + + + + + MyArduino + COM3 + 57600 + 19 + + + + + MyArduino + 1 + + + + + 500 + + + + MySignal + + + MySignal + + + Output1 + + + + Source1 + + + isON? + + + + Source1 + + + + + + + + + + + ON + + + + + + + + + + + + + MySignal + + + Output2 + + + + + Source1 + + + isOFF? + + + + Source1 + + + + + + + + + + + + + OFF + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/workflows/ArduinoOneToManyRefactored_withanalog.svg b/workflows/ArduinoOneToManyRefactored_withanalog.svg new file mode 100644 index 00000000..1e87a01c --- /dev/null +++ b/workflows/ArduinoOneToManyRefactored_withanalog.svg @@ -0,0 +1,3 @@ + +]>MyArduinoMySignalOutput1Output2GreaterThanMySignalMySignalAnalogInput \ No newline at end of file diff --git a/workflows/ArduinoRoundTrip.bonsai b/workflows/ArduinoRoundTrip.bonsai new file mode 100644 index 00000000..79e55dcb --- /dev/null +++ b/workflows/ArduinoRoundTrip.bonsai @@ -0,0 +1,44 @@ + + + + + + + MyArduino + COM3 + 57600 + 19 + + + + + MyArduino + 7 + + + + + + MyArduino + 6 + + + + + + + Interval.TotalMilliseconds + + + + + + + + + + \ No newline at end of file diff --git a/workflows/ArduinoRoundTrip.svg b/workflows/ArduinoRoundTrip.svg new file mode 100644 index 00000000..cb2c6737 --- /dev/null +++ b/workflows/ArduinoRoundTrip.svg @@ -0,0 +1,3 @@ + +]>MyArduinoInterval.TotalMillisecondsTimeIntervalDigitalOutputBitwiseNotDigitalInput \ No newline at end of file diff --git a/workflows/ArduinoServo.bonsai b/workflows/ArduinoServo.bonsai new file mode 100644 index 00000000..1b6f0b7b --- /dev/null +++ b/workflows/ArduinoServo.bonsai @@ -0,0 +1,32 @@ + + + + + + + MyArduino + COM3 + 57600 + 19 + + + + + 90 + + + + + MyArduino + 9 + + + + + + + + \ No newline at end of file diff --git a/workflows/ArduinoServo.svg b/workflows/ArduinoServo.svg new file mode 100644 index 00000000..85e16092 --- /dev/null +++ b/workflows/ArduinoServo.svg @@ -0,0 +1,3 @@ + +]>MyArduinoServoOutputInt \ No newline at end of file From 5484d27c579adb2c9e842c2ef7b2ef5b33bc275a Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Thu, 7 Jul 2022 11:44:54 +0100 Subject: [PATCH 13/13] Add subject-related workflows. --- articles/ArduinoArticle.md | 16 ++- workflows/ArduinoManyToOne.bonsai | 57 +++++++++ workflows/ArduinoManyToOne.bonsai.layout | 89 ++++++++++++++ workflows/ArduinoManyToOne.svg | 3 + workflows/ArduinoManyToOneRefactored.bonsai | 62 ++++++++++ .../ArduinoManyToOneRefactored.bonsai.layout | 113 ++++++++++++++++++ workflows/ArduinoManyToOneRefactored.svg | 3 + 7 files changed, 339 insertions(+), 4 deletions(-) create mode 100644 workflows/ArduinoManyToOne.bonsai create mode 100644 workflows/ArduinoManyToOne.bonsai.layout create mode 100644 workflows/ArduinoManyToOne.svg create mode 100644 workflows/ArduinoManyToOneRefactored.bonsai create mode 100644 workflows/ArduinoManyToOneRefactored.bonsai.layout create mode 100644 workflows/ArduinoManyToOneRefactored.svg diff --git a/articles/ArduinoArticle.md b/articles/ArduinoArticle.md index 7aaa1027..506d033b 100644 --- a/articles/ArduinoArticle.md +++ b/articles/ArduinoArticle.md @@ -158,14 +158,22 @@ As you can probably tell, as long as the downstream branches are subscribed to ` ![One-to-many refactored with analog input](~/workflows/ArduinoOneToManyRefactored_withanalog.bonsai) ::: -#### Subjects afford One-to-many logic "many-to-one" logic +#### Subjects afford "many-to-one" logic +Related to the previous example, very often, we find the need to have multiple computations converging on to a single output. For instance, two independent conditions might be allowed to change the state of an output signal. If we were to do it without subjects, one way to achieve this would be: + +:::workflow +![Many-to-one](~/workflows/ArduinoManyToOne.bonsai) +::: -## Alternatives to Firmata -### An example of a simple Serial communication protocol +We can refactor the workflow by creating a `Subject Source`. Right-click on top of [**`DigitalOutput`**](xref:Bonsai.Arduino.DigitalOutput) -> `Create Source` -> `Behavior Subject` and connect this source to our [**`DigitalOutput`**](xref:Bonsai.Arduino.DigitalOutput). We can then change the state of our digital output pin by simply sending events to the newly created `MyControlSignal` `Subject` using a `MulticastSubject` operator. + +:::workflow +![Many-to-one refactored](~/workflows/ArduinoManyToOneRefactored.bonsai) +::: -## General references: +## References: - https://github.com/firmata/arduino diff --git a/workflows/ArduinoManyToOne.bonsai b/workflows/ArduinoManyToOne.bonsai new file mode 100644 index 00000000..54b16460 --- /dev/null +++ b/workflows/ArduinoManyToOne.bonsai @@ -0,0 +1,57 @@ + + + + + + + MyArduino + COM3 + 57600 + 19 + + + + + false + + + + + PT1S + + + + + MyArduino + 6 + + + + + true + + + + + PT5S + + + + + MyArduino + 6 + + + + + + + + + + + \ No newline at end of file diff --git a/workflows/ArduinoManyToOne.bonsai.layout b/workflows/ArduinoManyToOne.bonsai.layout new file mode 100644 index 00000000..335cb941 --- /dev/null +++ b/workflows/ArduinoManyToOne.bonsai.layout @@ -0,0 +1,89 @@ + + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + \ No newline at end of file diff --git a/workflows/ArduinoManyToOne.svg b/workflows/ArduinoManyToOne.svg new file mode 100644 index 00000000..37da0b05 --- /dev/null +++ b/workflows/ArduinoManyToOne.svg @@ -0,0 +1,3 @@ + +]>MyArduinoDigitalOutputDigitalOutputDelayDelayBooleanBoolean \ No newline at end of file diff --git a/workflows/ArduinoManyToOneRefactored.bonsai b/workflows/ArduinoManyToOneRefactored.bonsai new file mode 100644 index 00000000..9838e681 --- /dev/null +++ b/workflows/ArduinoManyToOneRefactored.bonsai @@ -0,0 +1,62 @@ + + + + + + + MyArduino + COM3 + 57600 + 19 + + + + MyControlSignal + + + + MyArduino + 6 + + + + + false + + + + + PT1S + + + + MyControlSignal + + + + true + + + + + PT5S + + + + MyControlSignal + + + + + + + + + + + \ No newline at end of file diff --git a/workflows/ArduinoManyToOneRefactored.bonsai.layout b/workflows/ArduinoManyToOneRefactored.bonsai.layout new file mode 100644 index 00000000..1cd0cc8e --- /dev/null +++ b/workflows/ArduinoManyToOneRefactored.bonsai.layout @@ -0,0 +1,113 @@ + + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + \ No newline at end of file diff --git a/workflows/ArduinoManyToOneRefactored.svg b/workflows/ArduinoManyToOneRefactored.svg new file mode 100644 index 00000000..a001694f --- /dev/null +++ b/workflows/ArduinoManyToOneRefactored.svg @@ -0,0 +1,3 @@ + +]>MyArduinoDigitalOutputMyControlSignalMyControlSignalMyControlSignalDelayDelayBooleanBoolean \ No newline at end of file