# 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 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
 * 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/
 * NOTE: This example uses nlohmann/json(https://json.nlohmann.me/) which is a header only library
 *       to serialize and deserialize JSON body.
 */
#include <iostream>
#include <curl/curl.h>
#include "nlohmann/json.hpp"

using json = nlohmann::json;

std::string ipAddress = "192.168.73.1";
std::string clientKey = "";

std::string buildURL(const std::string &endpoint)
{
    std::ostringstream urlStream;
    urlStream << "http://" << ipAddress << "/api/" << endpoint;
    return urlStream.str();
}

json executeHttpRequest(CURL *curl, const std::string &url, const std::string &response)
{
    CURLcode resCode = curl_easy_perform(curl);

    if (resCode != CURLE_OK)
    {
        fprintf(stderr, "Error occurred while performing %s. Error: %s", url.c_str(), curl_easy_strerror(resCode));
        std::exit(-1);
    }
    else
    {
        long httpCode;
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
        if (httpCode != 200)
        {
            fprintf(stderr, "Error occurred while performing %s. HTTP Code: %ld", url.c_str(), httpCode);
            std::exit(-1);
        }
        json apiResponse = json::parse(response);
        if (!apiResponse["success"])
        {
            std::ostringstream errorStream;
            errorStream << apiResponse["code"] << ": " << apiResponse["messages"].dump();
            fprintf(stderr, "%s", errorStream.str().c_str());
            std::exit(-1);
        }
        return apiResponse["data"];
    }
}

size_t WriteCallback(void *contents, size_t size, size_t nmemb, std::string *response)
{
    size_t totalSize = size * nmemb;
    response->append(static_cast<char *>(contents), totalSize);
    return totalSize;
}

json httpGet(CURL *curl, const std::string &url)
{
    CURLcode resCode;
    std::string response;

    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

    return executeHttpRequest(curl, url, response);
}

json httpPost(CURL *curl, const std::string &url, json requestBody)
{
    CURLcode resCode;
    std::string response;

    auto postData = requestBody.dump();

    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_POST, 1);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, postData.size());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

    return executeHttpRequest(curl, url, response);
}

int main()
{
    CURL *curl;
    CURLcode res;

    curl = curl_easy_init();

    if (curl)
    {
        {
            /*
             * 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.
             */
            json claimReq;
            claimReq["force_connect"] = true;
            clientKey = httpPost(curl, buildURL("moku/claim_ownership"), claimReq);

            /*
             * 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 headers of
             * the curl object
             */
            struct curl_slist *headers = NULL;
            std::ostringstream clientKeyHeader;
            clientKeyHeader << "Moku-Client-Key: " << clientKey;

            headers = curl_slist_append(headers, clientKeyHeader.str().c_str());
            headers = curl_slist_append(headers, "Content-Type: application/json");

            curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

            printf("Established connection, client key: %s\n", clientKey.c_str());
        }

        {
            // Get the Moku's name
            auto result = httpGet(curl, buildURL("moku/name"));
            std::cout << result << std::endl;
        }

        {
            // Get the Moku's serial
            auto result = httpGet(curl, buildURL("moku/serial_number"));
            std::cout << result << std::endl;
        }

        {
            json frontendRequest = {{"channel", 1}, {"coupling", "DC"}, {"impedance", "1MOhm"}, {"range", "10Vpp"}};

            auto result = httpPost(curl, buildURL("slot1/oscilloscope/set_frontend"), frontendRequest);
            std::cout << result << std::endl;
        }

        {
            // Get data
            json getDataRequest = {{"wait_reacquire", true}};

            auto result = httpPost(curl, buildURL("slot1/oscilloscope/get_data"), getDataRequest);
            std::cout << result << std::endl;
        }

        // Clean up
        curl_easy_cleanup(curl);
    }

    // Clean up global cURL resources
    curl_global_cleanup();

    return 0;
}
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171

# 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