Assembling the circuits
- Each Sharp IR proximity sensor has three pins. One is the power input, which we connect to 3.3V. Another is the ground that we will connect to one GND pin. Lastly, there is the analog output that varies from 3.1V at 10cm to 0.4V at 80cm. The analog output pin needs to be connected to an analog input. Here, we used pin A5.
- Piezo buzzer uses a special crystal that expands and contracts as an electrical signal passes through it. This will generate a tone that we can hear. One pin of the piezo buzzer goes to GND connection and the other to digital pin D7 (or another pin with PWM feature if you use Spark Core, such as A4).
- A rotary adjustable potentiometer has three pins. Connect 3.3V to an outer pin, GND to the other, and the center pin will have a voltage that varies from 0 to 3.3V depending on the rotation of the pot. Hook the center pin to an ADC on a microcontroller and get a variable input from the user! Here, we used pin A2.
- To build the red LED circuit, connect one end of the resistor to pin D8 (or D7 on Spark Core). Connect the long leg of the LED (the positive leg, called the anode) to the other end of the resistor. Connect the short leg of the LED (the negative leg, called the cathode) to the GND.
- To build the green LED circuit, do as for the red LED circuit but connect the resistor to pin D6.
Programming the board with VIPER
Using VIPER is very easy! Let’s see how step by step:
- Download the Windows, Linux or iOS installers from VIPER Download page(security messages can be shown on Windows, please accept and go ahead, this issue will be solved soon).
- Install it (here’s a tutorial on how to install VIPER IDE) and launch it through the VIPER shortcut.
- Create a VIPER user through the dedicated button. Check your email and verify your new account by clicking on the provided link. Why registering? With a VIPER account you can: join the VIPER community; save your projects on the cloud and access them from different devices; automatically receive updates of the VIPER IDE, VIPER VM, Libraries, and new examples.
- Once the account has been verified the VIPER IDE automatically logs you into the VIPER cloud (the first time you create a user account an IDE restart can be required. If you have sign-in issue please restart the IDE).
- Connect your board and rename it as you prefer in order to easily recognize it in future.
- To make the board usable, you need to Viperize it. Viperization is the process of installing the VIPER VM (Virtual Machine) on a board. The process can be launched by clicking on the dedicated button available on the VIPER IDE top bar.
- Create a new project, copy the code from github, compile your script and upload it in the board. Follow the IDE messages, some boards require a manual reset during the upload process.
- Enjoy your running VIPER script!
Explaining the code
Why using VIPER
One of the concepts many people find challenging when beginning to write code for microcontrollers is how to manage multiple hardware-related tasks, seemingly all running at the same time. Designers are consequently frustrated by the difficulties in implementing such functionalities in microcontrollers.
In order to solve these pains, VIPER supports all the most used high-level features of Python like modules, classes, multithreading, callbacks, timers and exceptions, plus some custom hardware-related features like interrupts, PWM, digital I/O, etc.
Each thread in VIPER is a sort of separated and parallel process that runs autonomously on your board. A thread requires a function to be executed as input for the definition. The same function can be instanced by various thread giving you the possibility to write very concise and readable code. With threads you can design your algorithm architecture assuming parallelism that is typical of high level. More info here.
Inside the code
The script is implemented using 4 threads that run in parallel. One thread is used for acquiring and normalize the analog signals acquired through a potentiometer and a IR proximity sensor. The other three threads are used to instantiate a generic blink() function that drives two LEDs at different frequencies and a generic buzz() function that drives a buzzer at different frequency e length of the sleep (to create a "beat" effect), calculated on the basis of the acquired analog signals.
Get the script from github. The code has a ton of comments. Just a couple of notes.
- delay() vs. sleep()
In Arduino/Wiring using delay() has a side effect - the Arduino does nothing for that while. To get two or more "actions" to run independent of each other, you cannot use delay().
In VIPER the sleep() function suspends the current thread for time expressed in time_units BUT all the other threads are free to continue their execution!
- VIPER built-in functions
VIPER VM extends Python with built-in functions to handle the General Purpose Input Output pins of the embedded device. These functions resemble the ones used by Arduino, but are more flexible.
analogRead() vs. adc.read()
The analogRead() function is provided as a built-in to ease the passage from the Arduino/Wiring to VIPER.
However the preferred way to read an analog pin in VIPER is:
# import the adc driver
x = adc.read(pin, samples=1)
Reads analog values from pin that must be one of the Ax pins. If samples is 1 or not given, returns the integer value read from pin. If samples is greater than 1, returns a tuple of integers of size samples.
analogWrite() vs. pwm.write()
The Arduino's analogWrite() function provides a simple interface to the hardware PWM, but doesn't provide any control over frequency. The analogWrite() function is provided as a built-in to ease the passage from the Arduino/Wiring to VIPER. However the preferred way to use pwm in VIPER is:
# import the pwm driver
pwm.write(pin, period, pulse, time_unit=MILLIS)
The state of pin is periodically switched between LOW and HIGH according to parameters:
- period is the duration of a pwm square wave
- pulse is the time the pwm square wave stays in the HIGH state
- time_unit is the unit of time period and pulse are expressed in time_unit
You can start from this very simple example code to develop the behavior you prefer. Of course there is plenty of room for improvement, such as:
- adding a volume control
- taking advantage of the smartSensors library instead of mapping the analog input values range by hand
Feel free to suggest your own ideas, and have fun annoying people with your brand new multithreaded blinking theremin powered by VIPER!
Luigi F. Cerfeda (@L_F_Cerfeda) - VIPER team