# Other Languages

For details on what each of these examples are doing, please refer to our Getting Started with cURL/REST page.

If you don't see your language on the list, please reach out to our Support Engineers.

# C#

/*
    * Simple C# example on how to use the Moku REST API for Liquid Instrument 
    * Moku Devices.
    * IMPORTANT: Deploy the Oscilloscope through the Desktop or iPad apps before
    *        running this script in order to transfer the instrument data. See
    *        https://apis.liquidinstruments.com/starting-curl.html#first-steps
    * NOTE: This example demonstrates how to deploy and interact with an 
    *       Oscilloscope. Details on list of methods and associated request 
    *       schemas can be found at 
    *       https://apis.liquidinstruments.com/reference/oscilloscope/ 
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading.Tasks;

class MokuDemo
{
    const string IP_ADDRESS = "192.168.73.1";
    static Uri baseUri = new Uri(String.Format("http://{0}/api/", IP_ADDRESS));

    static async Task Main(string[] args)
    {
        /* Create a HttpClient object, we will be using the 
         * same object to send every request */

        HttpClient client = new HttpClient();
        client.BaseAddress = baseUri;
        client.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/json"));

        /*
          * The first thing we need to do is to take ownership of the Moku.
          * We do that by sending a `POST` to the `claim_ownership` resource 
          * and it gives us back a key that we can include in all future 
          * requests to prove we are the owner.
        */

        HttpResponseMessage clientKeyResponse = await client.PostAsync(
            "moku/claim_ownership", null);
        if (clientKeyResponse.IsSuccessStatusCode)
        {
            IEnumerable<string> headerValues = new string[] { };
            clientKeyResponse.Headers.TryGetValues("Moku-Client-Key", out headerValues);
            /* 
             * Now that we obtained the clientKey, we include it as
             * part of header for every subsequent request proving
             * the ownership.
             * Let's do that by adding it to the DefaultRequestHeaders of
             * the HttpClient object
             */
            client.DefaultRequestHeaders.Add("Moku-Client-Key", headerValues.First());
        }
        else
        {
            Console.WriteLine("Unexpected error: HTTP Response Code {0}", clientKeyResponse.StatusCode);
        }

        // Get the Moku's name

        HttpResponseMessage nameResponse = await client.GetAsync("moku/name");
        if (nameResponse.IsSuccessStatusCode)
        {
            string responseContent = nameResponse.Content.ReadAsStringAsync().Result;
            var nameObject = JsonDocument.Parse(responseContent);
            Console.WriteLine("Moku name is '{0}'", nameObject.RootElement.GetProperty("data"));
        }
        else
        {
            Console.WriteLine("Unexpected error: HTTP Response Code {0}", nameResponse.StatusCode);
        }

        /*
         * As you can see, all responses from the Moku are formatted as a JSON
         * dictionary
         * with four properties. `success` can be true or false depending whether what
         * you asked for was valid. It it's true, `data` contains the value(s) you asked
         * for.
         * If it's false then `code` and `messages` tell you why.
         *
         * The first time you access an instrument's resource, that instrument is
         * deployed.
         * Here we set the Oscilloscope frontend, implicitly deploying it first.
         */
        var frontendRequest = new
        {
            channel = 1,
            range = "10Vpp",
            coupling = "AC",
            impedance = "1MOhm"
        };
        StringContent frontendRequestContent = new StringContent(JsonSerializer.Serialize(frontendRequest));
        HttpResponseMessage frontendResponse = await client.PostAsync("oscilloscope/set_frontend", frontendRequestContent);
        if (frontendResponse.IsSuccessStatusCode)
        {
            string responseContent = frontendResponse.Content.ReadAsStringAsync().Result;
            var frontendObject = JsonDocument.Parse(responseContent).RootElement.GetProperty("data");
            var frontendParams = JsonSerializer.Deserialize<JsonDocument>(frontendObject.ToString());
            Console.WriteLine("Input range configured to : {0}", frontendParams.RootElement.GetProperty("range"));
        }
        else
        {
            Console.WriteLine("Unexpected error: HTTP Response Code {0}", frontendResponse.StatusCode);
        }

        //Get Data
        StringContent getDataContent = new StringContent(JsonSerializer.Serialize(new { wait_reacquire = false }));
        HttpResponseMessage getDataResponse = await client.PostAsync("oscilloscope/get_data", getDataContent);
        if (getDataResponse.IsSuccessStatusCode)
        {
            string responseContent = getDataResponse.Content.ReadAsStringAsync().Result;
            var getDataObject = JsonDocument.Parse(responseContent).RootElement.GetProperty("data");

            Console.WriteLine("Channel1 data: {0}", getDataObject.GetProperty("ch1"));
        }
        else
        {
            Console.WriteLine("Unexpected error: HTTP Response Code {0}", getDataResponse.StatusCode);
        }

    }
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

# Go

// Simple example of interacting with a Moku REST API from Go.
// This prioritises simplicity over, e.g. type safety so request bodies are formed
// as string and returns are unmarshalled to maps rather than structs.

package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

// These fields are case-insensitively matched against the JSON field names and therefore
// capitalized according to Go convention rather than exactly matching the JSON.
type moku_response struct {
	Success  bool
	Data     interface{}
	Messages []string
	Code     string
}


func do_rest_request(base_url, client_key, endpoint string, body []byte) (interface{}, error) {
	client := &http.Client{}

	req, err := http.NewRequest("POST", base_url+endpoint, bytes.NewReader(body))
	if err != nil {
		return nil, err
	}
	req.Header.Add("content-type", "application/json")

	if client_key != "" {
		req.Header.Add("Moku-Client-Key", client_key)
	}

	resp, err := client.Do(req)

	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	respBody, readErr := ioutil.ReadAll(resp.Body)
	if readErr != nil {
		return nil, readErr
	}

	response := moku_response{}
	jsonErr := json.Unmarshal(respBody, &response)

	if jsonErr != nil {
		return nil, jsonErr
	}

	if response.Success == false {
		return nil, errors.New(response.Messages[0])
	}

	return response.Data, nil
}


func take_ownership(base_url string) (string, error) {
	data, err := do_rest_request(base_url, "", "moku/claim_ownership", nil)

	if err != nil {
		return "", err
	}

	return data.(string), nil
}


func relinquish_ownership(base_url, client_key string) error {
	_, err := do_rest_request(base_url, client_key, "moku/relinquish_ownership", nil)
	return err
}


func main() {
	// Change your IP address here
	base_url := "http://192.168.73.1/api/"

	// The data returned from the take ownership request is exactly the client key (as well as the key
	// being present in a header)
	client_key, err := take_ownership(base_url)
	if err != nil {
		log.Fatal(err)
	}
	defer relinquish_ownership(base_url, client_key)

	// Now that we have ownership, and a client key to prove it, we can issue requests to any other endpoint we like.
	// Here the JSON body is formed directly as a byte array, you can also use the JSON library to marshall a struct
	// with the parameters if you prefer to keep strong typing on the requests.
	_, err = do_rest_request(base_url, client_key, "oscilloscope/set_frontend",
		[]byte(`{"channel": 1, "impedance": "1MOhm", "coupling": "AC", "range": "10Vpp"}`))


	data, err := do_rest_request(base_url, client_key, "oscilloscope/get_data", []byte(`{"wait_reacquire": false}`))

	// At this point, the actual request-specific response data (right now, frame data) is just in a map.
	// If you want to maintain type safety, you can:
	// 1. Open-code more of the do_rest_request so you can pass a response-specific struct to the unmarshalling
	// 2. Re-structure it with e.g https://github.com/mitchellh/mapstructure
	// 3. Make do_rest_response generic on the return structure (requires Go 1.18+, still in beta at the time of writing)
	fmt.Println(data.(map[string]interface{})["ch1"])
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

# Java


/*
    * Simple Java example on how to use the Moku REST API for Liquid Instrument Moku Devices.
    * IMPORTANT: Deploy the Oscilloscope through the Desktop or iPad apps before
    *        running this script in order to transfer the instrument data. See
    *        https://apis.liquidinstruments.com/starting-curl.html#first-steps
    * NOTE: This example uses cliftonlabs json-simple library to serialize/de-serialize request 
    *      and response. For more information, please visit https://cliftonlabs.github.io/json-simple/
    * NOTE: This example demonstrates how to deploy and interact with an Oscilloscope. 
    *       Details on list of methods and associated request schemas can be found at 
    *       https://apis.liquidinstruments.com/reference/oscilloscope/ 
*/
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.util.Map;
import static java.util.Map.entry;
import java.net.http.HttpRequest.BodyPublishers;
import com.github.cliftonlabs.json_simple.JsonObject;
import com.github.cliftonlabs.json_simple.Jsoner;

public class MokuDemo {

    static String ipAddress = "192.168.73.1";

    private static JsonObject executeAPICall(HttpRequest request, HttpClient client) throws Exception {
        HttpResponse<String> getDataResponse = client.send(request, BodyHandlers.ofString());
        if (getDataResponse.statusCode() != 200) {
            String errorText = String.format("Error occurred. Response code: %d", getDataResponse.statusCode());
            throw new Exception(errorText);
        }
        JsonObject data = (JsonObject) Jsoner.deserialize(getDataResponse.body());
        if (!(Boolean) data.get("success")) {
            System.out.println(data.get("messages"));
        }
        Object returnData = data.get("data");
        if (returnData.getClass().getName() == "java.lang.String") {
            return (JsonObject) Jsoner.deserialize((String) returnData);
        }
        return (JsonObject) returnData;
    }

    public static void main(String[] args) throws Exception {
        HttpClient client = HttpClient.newHttpClient();

        /*
         * The first thing we need to do is to take ownership of the Moku.
         * We do that by sending a `POST` to the `claim_ownership` resource and it
         * gives us back a key that we can include in all future requests to prove we
         * are the owner.
         */
        String clientKeyURI = String.format("http://%s/api/moku/claim_ownership", ipAddress);
        HttpRequest clientKeyRequest = HttpRequest.newBuilder()
                .uri(URI.create(clientKeyURI))
                .headers("Content-Type", "application/json")
                .POST(BodyPublishers.noBody())
                .build();
        HttpResponse<String> clientKeyResponse = client.send(clientKeyRequest, BodyHandlers.ofString());
        String clientKey = clientKeyResponse.headers().firstValue("Moku-Client-Key").orElseThrow();

        // Get the Moku's name
        String mokuNameURI = String.format("http://%s/api/moku/name", ipAddress);
        HttpRequest nameRequest = HttpRequest.newBuilder()
                .uri(URI.create(mokuNameURI))
                .headers("Content-Type", "application/json")
                .headers("Moku-Client-Key", clientKey)
                .GET()
                .build();
        HttpResponse<String> nameResponse = client.send(nameRequest, BodyHandlers.ofString());
        System.out.println(nameResponse.body());

        /*
         * As you can see, all responses from the Moku are formatted as a JSON
         * dictionary
         * with four properties. `success` can be true or false depending whether what
         * you asked for was valid. It it's true, `data` contains the value(s) you asked
         * for.
         * If it's false then `code` and `messages` tell you why.
         *
         * The first time you access an instrument's resource, that instrument is
         * deployed.
         * Here we set the Oscilloscope frontend, implicitly deploying it first.
         */

        JsonObject obj = new JsonObject().putAllChain(Map.ofEntries(
                entry("channel", 1),
                entry("range", "10Vpp"),
                entry("coupling", "AC"),
                entry("impedance", "1MOhm")));

        String frontendURI = String.format("http://%s/api/oscilloscope/set_frontend", ipAddress);
        HttpRequest frontendRequest = HttpRequest.newBuilder()
                .uri(URI.create(frontendURI))
                .headers("Content-Type", "application/json")
                .headers("Moku-Client-Key", clientKey)
                .POST(BodyPublishers.ofString(obj.toJson()))
                .build();
        JsonObject frontendResponse = executeAPICall(frontendRequest, client);
        // Print the impedance deserialized from response object
        System.out.println(frontendResponse.get("impedance"));

        /*
         * Now that instrument is deployed, we can read out a frame of data using the
         * get_data method which returns time series data for channels 1 & 2.
         */
        JsonObject getDataRequestBody = new JsonObject().putAllChain(
                Map.ofEntries(entry("wait_reacquire", true)));
        String getDataURI = String.format("http://%s/api/oscilloscope/get_data", ipAddress);
        HttpRequest getDataRequest = HttpRequest.newBuilder()
                .uri(URI.create(getDataURI))
                .headers("Content-Type", "application/json")
                .headers("Moku-Client-Key", clientKey)
                .POST(BodyPublishers.ofString(getDataRequestBody.toJson()))
                .build();
        JsonObject getDataResponse = executeAPICall(getDataRequest, client);
        System.out.println(getDataResponse.get("ch1"));
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

# Javascript (Node.js)


/**
 * Let's Moku!
 *
 * We're going to poke and prod a little bit at the Moku's API server directly
 * using just node.js's `request` library. The below code assumes you are connected
 * to your Moku's WiFi access point. If this isn't true, replace the IP address
 * below with your Moku's address as read out of the Moku desktop app.
 *
 * This can be run using `node moku-node.js` with node v14+
 * First, run `npm install --save request`
**/

const request = require('request')

// Helper function for making HTTP requests asynchronously
// options is a dict containing url, method, headers dict (optional), body (optional)
async function makeRequest(options) {
    return new Promise((resolve, reject) => {
        request(options, (err, response, body) => {
            if (err) reject(err)
            else resolve({ body, response })
        })
    })
}

async function main() {
    /*
     * The first thing we need to do is to take ownership of the Moku.
     * We do that by sending a `POST` to the `claim_ownership` resource and it
     * gives us back a key that we can include in all future requests to prove we
     * are the owner.
     */
    const IP_ADDRESS = '192.168.73.1'
    let response = await makeRequest({
        url: `http://${IP_ADDRESS}/api/moku/claim_ownership`,
        method: 'POST',
    })
    console.log(JSON.parse(response.body))
    const clientKey = response.response.headers['moku-client-key']

    // Get the Moku's name
    response = await makeRequest({
        url: `http://${IP_ADDRESS}/api/moku/name`, method: 'GET',
        headers: { 'Moku-Client-Key': clientKey }
    })
    console.log(JSON.parse(response.body))

    /*
     * As you can see, all responses from the Moku are formatted as a JSON dictionary
     * with four properties. `success` can be true or false depending whether what
     * you asked for was valid. It it's true, `data` contains the value(s) you asked for.
     * If it's false then `code` and `messages` tell you why.
     *
     * The first time you access an instrument's resource, that instrument is deployed.
     * Here we set the Oscilloscope frontend, implicitly deploying it first.
     */

    response = await makeRequest({
        url: `http://${IP_ADDRESS}/api/oscilloscope/set_frontend`,
        method: 'POST',
        body: JSON.stringify({ channel: 1, range: '10Vpp', coupling: 'AC', impedance: '1MOhm' }),
        headers: { 'Moku-Client-Key': clientKey }
    })
    console.log(JSON.parse(response.body))

    response = await makeRequest({
        url: `http://${IP_ADDRESS}/api/oscilloscope/get_data`,
        method: 'POST',
        body: JSON.stringify({ wait_reacquire: false }),
        headers: { 'Moku-Client-Key': clientKey }
    })
    // This prints out a frame dictionary like so:
    // { time: [-0.005, -0.004, -0.003, ...], ch1: [0.0, 0.0, 0.0, ...], ... }
    console.log(JSON.parse(response.body).data)
}

main()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

# Perl

# Simple example of using the Moku REST API for Liquid Instruments Moku devices.
# IMPORTANT: Deploy the Oscilloscope through the Desktop or iPad apps before
#            running this script in order to transfer the instrument data. See
#            https://apis.liquidinstruments.com/starting-curl.html#first-steps


use warnings;
use strict;

use Data::Dumper;

use JSON;
use REST::Client;

# Create a REST Client pointed at our Moku's IP address.
# All Moku REST endpoints expect (and return) JSON bodies, so we attach that header
# permanently to the client (rather than passing per-request).
my $client = REST::Client->new();
$client->setHost('http://192.168.73.1');
$client->addHeader('content-type', 'application/json');

# The first step with the Moku REST API is to claim ownership. A POST to this endpoint
# returns a client key which we also permanently attach to the client so it gets presented
# on each later call. You can also pass the request body '{"force_connect": true}' if you
# want to take over ownership regardless of the current ownership status.
my $key = $client->POST('/api/moku/claim_ownership')->responseHeader('Moku-Client-Key');
$client->addHeader('Moku-Client-Key', $key);

# Do a few operations targeting the Oscilloscope instrument. The bodies can be marshalled/
# /unmarshalled to/from JSON using the JSON library, or can be presented statically as a string
# if you prefer (e.g. if it's all constants anyway).
$client->POST('/api/oscilloscope/set_frontend',
              '{"channel": 1, "impedance": "1MOhm", "range": "10Vpp", "coupling": "DC"}'
             );

my $responseFrame = from_json $client->POST('/api/oscilloscope/get_data',
                                            '{"wait_reacquire": false}'
                                           )->responseContent();
my %dataFrame = %{$responseFrame->{data}};

# Just print the keys of the returned data frame, in a real program you'd do something with the data!
print Dumper(keys %dataFrame);

# Don't forget to relinquish ownership at the end. If you miss this line, e.g. because a line
# above croaks, then you (and anyone else) will need to force_connect next time around.
$client->POST('/api/moku/relinquish_ownership');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# Rust

use std::error::Error;

/**
 * Let's Moku!
 *
 * We're going to poke and prod a little bit at the Moku's API server directly
 * using just the `reqwest` and `json` Rust crates. The below code assumes you are connected
 * to your Moku's WiFi access point. If this isn't true, replace the IP address
 * below with your Moku's address as read out of the Moku desktop app.
 *
 * We use the `reqwest` crate to do blocking HTTP requests.
 * You'll need to add `reqwest` and `json` to your `Cargo.toml` file as follows:
 *   [dependencies]
 *   reqwest = { version = "0.11", features = ["blocking"] }
 *   json = { version = "0.12.4" }
 *
 * If you're starting a Rust project from scratch, you should:
 * 1. Run `cargo new mokurs` and `cd mokurs`
 * 2. Replace `src/main.rs` with the contents of this file
 * 3. Add the dependencies section above to `Cargo.toml`
 * 4. Run `cargo run`
**/

fn main() -> Result<(), Box<dyn Error>> {
    /*
     * The first thing we need to do is to take ownership of the Moku.
     * We do that by sending a `POST` to the `claim_ownership` resource and it
     * gives us back a key that we can include in all future requests to prove we
     * are the owner.
     */
    const IP_ADDRESS: &str = "192.168.73.1";
    let client = reqwest::blocking::Client::new();

    // We must supply an empty body, otherwise the request will be malformed
    let client_key: String = {
        let resp = client.post(format!("http://{IP_ADDRESS}/api/moku/claim_ownership"))
            .body("").send()?;
        let client_key: String = String::from(resp.headers().get("Moku-Client-Key").unwrap().to_str()?);
        println!("{}", json::parse(&resp.text()?[..])?);  // moves resp
        client_key
    };

    // Get the Moku's name
    {
        let resp = client.get(format!("http://{IP_ADDRESS}/api/moku/name"))
            .header("Moku-Client-Key", client_key.clone()).send()?;
        println!("{}", json::parse(&resp.text()?[..])?);
    }

    /*
     * As you can see, all responses from the Moku are formatted as a JSON dictionary
     * with four properties. `success` can be true or false depending whether what
     * you asked for was valid. It it's true, `data` contains the value(s) you asked for.
     * If it's false then `code` and `messages` tell you why.
     *
     * The first time you access an instrument's resource, that instrument is deployed.
     * Here we set the Oscilloscope frontend, implicitly deploying it first.
     */
    {
        let options = json::object!{
            channel: 1,
            range: "10Vpp",
            coupling: "AC",
            impedance: "1MOhm"
        };
        let resp = client.post(format!("http://{IP_ADDRESS}/api/oscilloscope/set_frontend"))
            .header("Moku-Client-Key", client_key.clone())
            .body(options.dump())
            .send()?;
        println!("{}", json::parse(&resp.text()?[..])?);
    }
    // Get a frame of data
    {
        let resp = client.post(format!("http://{IP_ADDRESS}/api/oscilloscope/get_data"))
            .header("Moku-Client-Key", client_key.clone())
            .body("{\"wait_reacquire\": false}")
            .send()?;
        let result: json::JsonValue = json::parse(&resp.text()?[..])?;
        // We print out result['data']['ch1'], which is an array of floats
        if let json::JsonValue::Object(res_obj) = result {
            if let json::JsonValue::Object(data_obj) = res_obj.get("data").unwrap() {
                println!("{}", data_obj.get("ch1").unwrap());
            }
        }
    }

    Ok(())
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88