Receive data via LTE on Boron

I want to send a string to a Boron over the LTE connection. Am I missing something obvious? Maybe I am not searching the correct terms. I am thinking it has something to do with Cloud API, but not sure. Can someone point be in the right direction?

I can't tell for sure, but it sounds like you might want to use a Particle function.

Hey @TRC_Doug ,

I’m assuming you want to send some information from your web application down to your device? If so, you can use the Particle.subscribe method in your firmware with a callback function to handle the string.

  1. You need to create a handler function that will process the incoming data. This function should have the following signature:

    void myHandler(const char *event, const char *data) {
        // Process the event data here
        if (data) {
            Serial.println(data); // Print or handle the received string
        }
    }
    
    
  2. In your setup() function, register the handler with a subscription to an event. For example:

    void setup() {
        Serial.begin(9600); // Start the serial communication
        Particle.subscribe("myEvent", myHandler); // Subscribe to the event
    }
    

Then, from the Cloud API, you can call a Particle.publish so that you device’s subscription handler triggers.

Edit: Or you can use a Particle.function and call that from the Cloud API

In response to Rick:

OK, I may be on the right track then. I just came across that myself! I am trying to control an array of i2c relays remotely using a Boron. I think I will be using a webpage to select which outputs to activate, then it communicates to the Particle cloud somehow or directly to the Boron.

I see how to register the function and use the String from this example: Particle.function() - Cloud functions | Reference | Particle

Next step is to figure out how to POST to that function. I will start here: Cloud API reference | Reference Documentation | Particle

Am I on the right track or is there a tutorial I could follow?

I just saw the post for Eric. I am reading that now!

Correct.

Calling cloud API from a web page may also be helpful.

In my console for this device, the function ‘lights’ is listed, and I can send commands. The expected action works!

However, I cannot get this web page to work (Calling cloud API from a web page). I have edited the html to match my function name ‘lights’, but it does not show in the devices list after I log in.

<html>

<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/particle-api-js@8/dist/particle.min.js"></script>

    <script>
        var particle = new Particle();
        let mfa_token;

        $(document).ready(function () {
            // This function is called when the page loads

            $('#loginForm').submit(function (e) {
                // The Login button on the login page was clicked (or Return pressed)
                e.preventDefault();

                // Hide the login page so the button goes away
                $('#loginDiv').css('display', 'none');
                $('#loginFailedDiv').css('display', 'none');
                sessionStorage.particleUser = $('#userInput').val();

                // Attempt to log into the Particle cloud
                $.ajax({
                    data: {
                        'client_id': 'particle',
                        'client_secret': 'particle',
                        'expires_in': 3600,
                        'grant_type': 'password',
                        'password': $('#passwordInput').val(),
                        'username': $('#userInput').val()
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        if (jqXHR.status === 403) {
                            // Got a 403 error, MFA required. Show the MFA/OTP page.
                            mfa_token = jqXHR.responseJSON.mfa_token;
                            $('#otpDiv').css('display', 'inline');
                            return;
                        }
                        console.log('error ' + textStatus, errorThrown);
                        $('#loginDiv').css('display', 'inline');
                        $('#loginFailedDiv').css('display', 'inline');
                    },
                    method: 'POST',
                    success: function (data) {
                        loginSuccess(data.access_token);
                    },
                    url: 'https://api.particle.io/oauth/token',
                });
            });

            $('#otpForm').submit(function (e) {
                // Login on the OTP/MFA form
                e.preventDefault();

                $('#otpDiv').css('display', 'none');

                $.ajax({
                    data: {
                        'client_id': 'particle',
                        'client_secret': 'particle',
                        'grant_type': 'urn:custom:mfa-otp',
                        'mfa_token': mfa_token,
                        'otp': $('#otpInput').val()
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        // Invalid MFA token
                        $('#loginDiv').css('display', 'inline');
                        $('#loginFailedDiv').css('display', 'inline');
                    },
                    method: 'POST',
                    success: function (data) {
                        loginSuccess(data.access_token);
                    },
                    url: 'https://api.particle.io/oauth/token',
                });

            });

            $('#logoutButton').on('click', function (e) {
                // Logout button clicked
                e.preventDefault();

                // Delete the access token from local session storage
                const accessToken = sessionStorage.particleToken;
                delete sessionStorage.particleToken;
                delete sessionStorage.particleUser;

                // Invalidate the token on the cloud side
                $.ajax({
                    data: {
                        'access_token': accessToken
                    },
                    method: 'DELETE',
                    complete: function () {
                        // Show the login page
                        $('#mainDiv').css('display', 'none');
                        $('#loginDiv').css('display', 'inline');
                        $('#loginFailedDiv').css('display', 'none');
                    },
                    url: 'https://api.particle.io/v1/access_tokens/current',
                });
            });
            $('#ledRedButton').on('click', function (e) {
                e.preventDefault();
                ledControl('Red');
            });
            $('#ledGreenButton').on('click', function (e) {
                e.preventDefault();
                ledControl('Green');
            });
            $('#ledYellowButton').on('click', function (e) {
                e.preventDefault();
                ledControl('Yellow');
            });
            $('#ledBlueButton').on('click', function (e) {
                e.preventDefault();
                ledControl('Blue');
            });
            $('#ledOrangeButton').on('click', function (e) {
                e.preventDefault();
                ledControl('Orange');
            });
            $('#ledMagentaButton').on('click', function (e) {
                e.preventDefault();
                ledControl('Magenta');
            });
            $('#ledWhiteButton').on('click', function (e) {
                e.preventDefault();
                ledControl('White');
            });

            if (sessionStorage.particleToken) {
                // We have a Particle access token in the session storage. Probably
                // refreshed the page, so try to use it. You don't need to log in
                // every time, you can reuse the access token if it has not expired.
                $('#loginDiv').css('display', 'none');
                getDevices();
            }
        });

        function loginSuccess(token) {
            sessionStorage.particleToken = token;
            getDevices();
        }

        function getDevices() {
            // Request the device list from the cloud
            particle.listDevices({ auth: sessionStorage.particleToken }).then(
                function (data) {
                    // Success! Show the main page
                    $('#mainDiv').css('display', 'inline');

                    // Load the device selector popup
                    loadDeviceList(data.body);
                },
                function (err) {
                    // Failed to retrieve the device list. The token may have expired
                    // so prompt for login again.
                    $('#mainDiv').css('display', 'none');
                    $('#loginDiv').css('display', 'inline');
                    $('#loginFailedDiv').css('display', 'inline');
                }
            );
        }

        function loadDeviceList(deviceList) {
            let html = '';

            $('#userSpan').text(sessionStorage.particleUser);

            deviceList.forEach(function (dev) {
                // For each device in the user's account, see if the device supports the "led" function call
                // Also note whether it's online or not.
                if (dev.functions.includes('lights')) {
                    html += '<option value="' + dev.id + '">' + dev.name + (dev.online ? '' : ' (offline)') + '</option>';
                }
            });
            $('#deviceSelect').html(html);

            if (html == '') {
                $('#statusSpan').text('No devices are running led control firmware');
            }
            else {
                $('#statusSpan').text('');
            }
        }

        function lights(cmd) {
            // Used to turn on or off the LED by using the Particle.function "led"
            const deviceId = $('#deviceSelect').val();

            $('#statusSpan').text('');

            particle.callFunction({ deviceId, name: 'lights', argument: cmd, auth: sessionStorage.particleToken }).then(
                function (data) {
                    $('#statusSpan').text('Call completed');
                },
                function (err) {
                    $('#statusSpan').text('Error calling device: ' + err);
                }
            );
        }
    </script>
</head>

<body>
    <div id="mainDiv" style="display: none;">
        <h3>Control the Color LED!</h3>
        <form>
            <p>Device: <select id="deviceSelect"></select></p>
            <p><button id="ledRedButton">Red</button>&nbsp;
				<button id="ledGreenButton">Green</button>&nbsp;
				<button id="ledBlueButton">Blue</button>&nbsp;
				<button id="ledYellowButton">Yellow</button>&nbsp;
				<button id="ledOrangeButton">Orange</button>&nbsp;
				<button id="ledMagentaButton">Magenta</button>&nbsp;
				<button id="ledWhiteButton">White</button>&nbsp;</p>
            <p>&nbsp;</p>
            <p><span id="statusSpan"></span></p>
            <p>&nbsp;</p>
            <p>Logged in as <span id="userSpan"></span> <button id="logoutButton">Log out</button></p>
        </form>
    </div>
    <div id="loginDiv">
        <h3>Login to Particle</h3>
        <div id="loginFailedDiv" style="display: none;">
            <p>Login failed, please try again.</p>
        </div>
        <form id="loginForm">
            <p>Username (Email): <input type="text" id="userInput" /></p>
            <p>Password: <input type="password" id="passwordInput" /></p>
            <p><input type="submit" value="Login" /></p>
        </form>
    </div>
    <div id="otpDiv" style="display: none;">
        <form id="otpForm">
            <p>One-time password from your authenticator app: <input type="text" id="otpInput" /></p>
            <p><input type="submit" value="Login" /></p>
        </form>
    </div>
</body>

</html>

I get the feeling that the web page is not actually “logging in” and that is why I don’t see any devices. I tested with purposely wrong credentials, and it looks the same. I get a blank drop down in the device list. Do I need to create some Could Service or API token first?

No, you do not need to create a token. Are there any errors in the Javascript debug console in your browser?

There are two warnings:

Those warnings always appear.

Is there a "No device are running led control firmware" message below the on/off buttons?

Does it say "Logged in as (your username)" at the bottom with a logout button?

I just ran the example firmware on an Argon and used the downloaded HTML sample code and it worked properly when opened as a file url (Chrome on Mac).

Is there a "No device are running led control firmware" message below the on/off buttons?
Does it say "Logged in as (your username)" at the bottom with a logout button?

Yes to both.

My function is called ‘lights’ and I am fairly confident that I replaced all the “ledControl” with “lights”.

Maybe I should test the unedited example first. I have tried it in both Brave and Chrome on Win11.

Earlier, I thought I tested a false password, but turns out my NumLock was off, so I didn’t actually change the password before hitting enter. I have now since tested a wrong password and is says Login failed, as expected.

You can also check the console or use the CLI particle list command to show the functions that are registered to devices.

Yes, as I mentioned earlier, I have a function called ‘lights’ in the console, and I can send it a command string and it responds properly.

In the CLI on the partical workbench in MS Code, it is trying to connect to my other product and says it is offline, even though I have setup the target device for this light controller using the product ID.

Do you literally mean it's a product, in the Products section of the console? If so, you need to modify the Javascript code in the htm file to use the product endpoint and specify the product ID of the product. The sandbox list devices API does not list devices that are in a product, even if it's a product in the same account.

No, sorry for the confusion. It is still in the sandbox:

Function is listed, and works:

I have another device that I have programmed in VS Code. It is trying to connect to that device instead of this new one, even though I have gave it the full device ID in the device setup.

That appears to be a sandbox product, Traffic Light Controller. You need to use the product APIs to access those devices.

If it was Sandbox > Devices > View Device, it would be a top-level sandbox device, which would work using the sample web page code.

1 Like

Thanks! I was pulling my hair out trying to find the problem in the .htm file. I will try to find a product API tutorial and proceed from there. Is that something I can integrate into this web page interface?

I removed the function loadDeviceList and hard coded my deviceId into the htm. IT WORKS! I will come up with a more secure solution later. The web interface will not be public in any way, so I may just keep it like this. I would rather use some sort of token that I could revoke or reset however. But for a proof of concept demonstration to management, I am satisfied!