Starting this week, you can use Yoctopuce modules directly from an application written in TypeScript. We have indeed just published a new programming library for this language, which is becoming more and more popular. It's therefore the occasion to offer you a tutorial for new users of Yoctopuce modules, with a short and simple but realistic example. As usual, we assume that you have some prior knowledge of TypeScript programming, but we'll explain the rest in detail.
As an application example, we are going to control a Yocto-PowerRelay-V3 depending on the temperature provided by a Yocto-Temperature. We assume that you have carefully read our previous posts in our for the beginners series, and in particular the post on the logical structure of Yoctopuce devices.
We want to change the state of the Yocto-PowerRelay-V3 depending on the temperature measured by the Yocto-Temperature
Installation
For this example, we are going to write an application designed to run with Node.js in command line. If you are rather interested by using it in a web browser, you can find additional explanations in the documentation and in the examples located in the zip file of our library... while waiting for another tutorial specifically dedicated to this issue.
The first thing that you need to do is thus to install on your machine both Node.js and TypeScript if you haven't already done so. You can download the latest version of Node.js for free from http://nodejs.org and install it. When this is done, to install TypeScript globally on your machine, simply run the command:
As JavaScript - and thus TypeScript - doesn't have direct access to the USB ports, access to Yoctopuce USB modules must be performed through the VirtualHub application, which you must also download from our web site and run before you start.
We are now going to build from scratch our test project in TypeScript for Node.js. Open a shell (a command window) in an empty directory, for example called tutorial . First, we are going to create the definition file of the project for NPM with the command:
Then, we are going to tell NPM that our project needs the Yoctopuce library in the shape of a CommonJS module:
Finally, we are going to create a configuration file for the TypeScript compiler, and indicate that we want the generated code to use the ECMAScript 2017 standard.
We now only have to write the TypeScript code. To do so, you can naturally use any text editor, but if you use an editor which specifically supports TypeScript, such as WebStorm or Visual Studio Code, as a bonus you will benefit from assistance during code writing, including instant documentation of Yoctopuce library method parameters.
Using the Yoctopuce library for TypeScript
Create a new file called index.ts and insert into it the following lines:
async function runDemo(): Promise<void>
{
let errmsg: YErrorMsg = new YErrorMsg;
await YAPI.RegisterHub("127.0.0.1", errmsg);
console.log('Connected to the VirtualHub !');
await YAPI.FreeAPI();
}
runDemo()
These few lines require some explanations, but don't worry, when you have understood what's behind them, everything else will be almost obvious :-)
1. Importing yocto_api
The basic functions of our library are traditionally included when importing the yocto_api file. However, the TypeScript library can be used both in an HTML browser and in a Node.js environment, but we necessarily don't go through the same network libraries in both cases. So we had to resign ourselves and propose two distinct import points: yocto_api_nodejs and yocto_api_html. You must select the appropriate one depending on the network library that you want to use. If part of your code must work in both modes, it can include only yocto_api. But at least one of the files of your project must include one of the two entry points linked to the network layer.
Note that the name of the library that we import is yoctolib-cjs, where cjs is the abbreviation for CommonJS, the module standard commonly used with Node.js. There is no explicit mention of TypeScript in the name of the library, because by definition a TypeScript module can be imported only compiled, therefore in pure JavaScript. Our new library can thus also be used by code written in pure JavaScript. Moreover, inside are the .d.ts type definition files enabling its use while benefitting from all the advantages of TypeScript strong typing.
Note that if you'd rather embed the library in TypeScript source code directly into your project, rather than importing a compiled module, you can find all the source files in a zip file on our page listing all the Yoctopuce libraries.
2. async functions and Promise objects
You will have noticed the async keyword before the declaration of our runDemo function. If you are not familiar yet with the use of asynchronous functions with async / await, you have to dive right into it to use the Yoctopuce library... It's not directly a specificity of Yoctopuce, as this syntax was introduced in the ECMAScript standard in 2017, but our library uses it almost everywhere. It enables us to avoid passing as parameters callback functions for each potentially blocking input/output call, as it was done in the past in JavaScript, which tended to fragment the code enormously.
Instead of this, by declaring an async function, we tell the compiler that the result of the function is deferred with the use of an implicit callback. Inside the function, we add the await keyword before calling any asynchronous function, which implicitly creates a callback function with the remainder of the code, which is automatically called after execution of the asynchronous command. Finally, the code is almost as simple as if were in a preemptive multi-thread environment, without having to manage a mutex. Except that...
Except that we must be wary that using an asynchronous function is catching: as the call to the function is deferred, the caller must itself be asynchronous, or at least use the .then method with a callback to retrieve the result of the Promise object.
3. API Initialization
await YAPI.RegisterHub("127.0.0.1", errmsg);
To use the Yoctopuce library, we must initialize it by telling it on which machines the modules are located. It's what the call to YAPI.RegisterHub does. The 127.0.0.1 address corresponds to the local machine, on which you have previously installed the VirtualHub to access the modules connected by USB.
As mentioned above, we prefix the call with the await keyword so that the code execution goes on only when the asynchronous operations are completed.
If the VirtualHub was not started on the machine, the call to RegisterHub insists a little, then triggers an exception. You can then find details of the error in the errmsg object. If you want to avoid exceptions, you can also disable the use of exceptions in the library, and retrieve the result as a return value. There is more information on this topic in the documentation.
3. Freeing the API
The call to RegisterHub opened a WebSocket communication channel to allow it to query the Yoctopuce modules. If you want your application to end, you must close this channel and end all the asynchronous operations that are linked to it. It's the purpose of the call to the asynchronous method YAPI.FreeAPI().
A more useful example
Now that the stage is set, we are going to write a small application which switches the relay when the temperature goes above a given threshold, let's say 25°C. Let's take it step by step.
1. Imports
You must first include the correct library imports. On top of the essential "yocto_api_nodejs.js", we intend to use a temperature sensor and a relay, so we also need "yocto_temperature.js" and "yocto_relay.js". We must therefore start with:
import { YTemperature } from 'yoctolib-cjs/yocto_temperature.js'
import { YRelay } from 'yoctolib-cjs/yocto_relay.js'
2. Function retrieval
As you have read the post on the logical structure of Yoctopuce devices, you know that the Yocto-Temperature and the Yocto-PowerRelay-V3 host different functions among which you can find temperature and relay. It's these two functions that we are interested in. There are several ways to retrieve them.
FirstXXX
If you are sure to have only one temperature function and only one relay function available, you can keep it simple and use the FirstXXX of the YTemperature and YRelay classes.
if(!temp) {
console.error("No temperature sensor found");
return;
}
let relay: YRelay | null = YRelay.FirstRelay();
if(!relay) {
console.error("No relay found");
return;
}
These methods return the first occurrence of the looked for function, or null if nothing corresponds.
FindXXX and serial number
You can also use the FindXXX method of the YTemperature and YRelay classes, with the serial number of the modules. Let's suppose that the serial number of the Yocto-Temperature is TMPSENS1-3EA8F and that the serial number of the Yocto-PowerRelay-V3 is RELAYHI3-56FC0, you can use:
if(!await temp.isOnline()) {
console.error("Temperature sensor not connected");
}
let relay: YRelay = YRelay.FindRelay("RELAYHI3-56FC0.relay1")
if(!await relay.isOnline()) {
console.error("Relay not connected");
}
In the opposite to FirstXXX, FindXXX always returns a valid object, therefore you must check whether it truly corresponds to a connected module with the isOnline() method. This allows you to instantiate objects corresponding to modules which could be connected later in the life of the program.
FindXXX and logical names
Instead of the serial number, you can use a logical name that you have previously assigned with the VirtualHub.
Assigning a logical name to the relay of the Yocto-PowerRelay
Let's assume that you called the relay "myRelay" and the temperature sensor "myTemp". Then, you only need to write:
if(!await temp.isOnline()) {
console.error("Temperature sensor 'myTemp' not found");
}
let relay: YRelay = YRelay.FindRelay("myRelay");
if(!await relay.isOnline()) {
console.error("Relay 'myRelay' not found");
}
The interest of logical names is twofold: first it allows you to more easily navigate in complex systems composed of many Yoctopuce modules, and secondly it allows you to build several copies of a system without having to adapt the code to the serial numbers of each copy.
3. The main loop
The main loop of our program serves to read the temperature of the sensor and to switch the position of the relay depending on the temperature:
let value: number = await temp.get_currentValue();
if(value > 25) {
await relay.set_state(YRelay.STATE_B);
} else if(value < 24) {
await relay.set_state(YRelay.STATE_A);
}
}
Note that we switch the relay into state B if the temperature is above 25, while we go back to state A if the temperature goes below 24. This is called a schmitt trigger and prevents the relay from oscillating when the temperature is too close to the trigger threshold.
We could stop here, but you don't necessarily want to run this loop at maximal speed. In any case, the refreshing rate of the Yocto-Temperature is 10Hz. We can therefore insert a 100ms pause in the loop without it affecting the system's reactivity.
Conclusion
We agree that this post is somewhat lengthy. But if you put together all the pieces of code we have described, you'll understand that using Yoctopuce modules in a TypeScript application requires in the end only a few lines of code:
import { YTemperature } from 'yoctolib-cjs/yocto_temperature.js'
import { YRelay } from 'yoctolib-cjs/yocto_relay.js'
async function runDemo(): Promise<void>
{
let errmsg: YErrorMsg = new YErrorMsg;
await YAPI.RegisterHub("127.0.0.1", errmsg);
let temp: YTemperature = YTemperature.FindTemperature("myTemp");
if(!await temp.isOnline()) {
console.error("Temperature sensor 'myTemp' not found");
}
let relay: YRelay = YRelay.FindRelay("myRelay");
if(!await relay.isOnline()) {
console.error("Relay 'myRelay' not found");
}
while(await temp.isOnline() && await relay.isOnline())
{
let value: number = await temp.get_currentValue();
if(value > 25) {
await relay.set_state(YRelay.STATE_B);
} else if(value < 24) {
await relay.set_state(YRelay.STATE_A);
}
await YAPI.Sleep(100,errmsg)
}
await YAPI.FreeAPI();
}
runDemo()
Finally, note that you can find an interactive documentation of the TypeScript Yoctopuce API in the Documentation directory of the library and on our web site.