Connecting Mendix To A Raspberry Pi Pico W Microcontroller

With more devices than ever being Internet enabled, I wanted to look at linking up a Mendix application to a microcontroller and seeing if I could control it remotely. I decided to use a Raspberry Pi Pico W.

What is the Raspberry Pi Pico W?

The Raspberry Pi Pico W is a microcontroller board that is an upgraded version of the original Raspberry Pi Pico, incorporating wireless capabilities, and costing less than £6. Released in 2022, the Pico W is built around the RP2040 microcontroller, a custom-designed chip from Raspberry Pi. It features a dual-core Arm Cortex-M0+ processor running at up to 133 MHz, 264 KB of SRAM, and 2 MB of onboard flash memory. This powerful combination allows it to handle a wide range of tasks, from controlling sensors and actuators to performing moderate computational tasks like signal processing.

One of the key features that sets the Pico W apart from its predecessor is the inclusion of wireless connectivity, specifically Wi-Fi. It uses the Infineon CYW43439 chip to provide 2.4 GHz 802.11n wireless networking. This opens up new possibilities for IoT (Internet of Things) applications, where devices need to communicate with each other or with the cloud. The wireless capability makes it ideal for projects like home automation, remote sensing, and wireless data logging, where connectivity is crucial.

Despite its powerful capabilities, the Raspberry Pi Pico W remains highly affordable, maintaining the same low-cost philosophy as other Raspberry Pi products. Its compact size and efficient power usage also make it suitable for battery-powered or embedded projects. The board supports a variety of programming languages, including MicroPython, C/C++, and CircuitPython, making it accessible to both beginners and experienced developers. Overall, the Raspberry Pi Pico W is a versatile, cost-effective solution for projects that require both local processing and wireless connectivity.

Building a simple REST service

In this example, I’m going to use MicroPython to create a REST service than my Mendix application can consume.

The Raspberry Pi Pico W has a couple of built in features we can expose to our REST service. The first is the built in LED. We can add REST endpoints to turn this on and off. The second is the built in Analog to Digital Converter (ADC) that we can use to take a temperature reading. We can add a REST endpoint to return the current temperature.

The easiest way to build a REST service on the Raspberry Pi Pico W that I have found is to use the Phew library. This allows us to define endpoints that run specific Python defs to return data.

You will need to install an application called Thonny on your computer to be able to upload the MicroPython code to your Raspberry Pi Pico W. You can find full instructions on the Thonny website. https://thonny.org/

This isn’t a MicroPython tutorial, so I won’t explain the code in depth. The main parts to look at are the endpoints and their return values. For example, I define the temperature I use the following line…

@server.route("/api/temperature", methods=["GET"])

This defines the endpoint to respond to GET requests to /api/temperature.

The data is returned as JSON using the following line…

return json.dumps({"temperature" : temperature}), 200, {"Content-Type": "application/json"}

There are similar endpoints and return values to turn the LED on and off.

Here’s the code to upload to your Raspberry Pi Pico W if you are following along.

from phew import server, connect_to_wifi
import machine
import json

# Connect to WIFI
ip = connect_to_wifi("YOUR-WIFI-NAME", "YOUR-WIFI-PASSWORD")

# Get the onboard LED
led = machine.Pin("LED", machine.Pin.OUT)

print("connected to IP ", ip)

# Setup the temperature endpoint. 
@server.route("/api/temperature", methods=["GET"])
def get_temperature(request):
    adc = machine.ADC(4)  # Use ADC pin GP4
    conversion_factor = 3.3 / (65535)  # ADC conversion factor
    sensor_value = adc.read_u16() * conversion_factor
    temperature = 27 - (sensor_value - 0.706) / 0.001721  # Convert sensor value to temperature (formula may vary)
    
    return json.dumps({"temperature" : temperature}), 200, {"Content-Type": "application/json"}

# Setup the LED ON endpoint.
@server.route("/api/led/on", methods=["GET"])
def ledCommand(request):
    led.value(1)
    return json.dumps({"message" : "Command sent successfully!"}), 200, {"Content-Type": "application/json"}

# Setup the LED OFF endpoint.
@server.route("/api/led/off", methods=["GET"])
def ledCommand(request):
    led.value(0)
    return json.dumps({"message" : "Command sent successfully!"}), 200, {"Content-Type": "application/json"}

# Setup a catchall for all other requests we can't handle.
@server.catchall()
def catchall(request):
    return json.dumps({"message" : "URL not found!"}), 404, {"Content-Type": "application/json"}

# Start the REST service.
server.run()

Upload and run this MicroPython program to your Raspberry Pi Pico W using Thonny. When you run it, it will connect to the specified Wifi network using the username and password you supplied in the code, and it will return an IP address that you can use in your Mendix application to talk to the Pico.

MPY: soft reboot
2024-09-30 21:17:28 [debug    / 163kB]   - connecting
2024-09-30 21:17:31 [debug    / 161kB]   - connecting
connected to IP  192.168.178.84
2024-09-30 21:17:33 [info     / 169kB] > starting web server on port 80

We can quickly test if everything is working by connecting to our REST service using a web browser. My Pico returned it was connected to 192.168.178.84 (your IP address may be different), so I can turn the LED on by going to http://192.168.178.84/api/led/on . The LED will turn on, and the following JSON will have been returned.

{"message": "Command sent successfully!"}

Now, lets write a Mendix application do this.

Building the Mendix application

Firstly, create a new Mendix application. I’ve called mine PicoWIOT.

Mendix has a new feature called Consumed REST Services which is currently in Beta. This makes it really easy to consume external web services. To use it, create a Consumed Web Service. I called mine CWS_PicoW.

In the Configuration and authentication tab, set the Base URL to be http://192.168.178.84/api/ . We don’t have any Authentication, so leave that as “No Authentication”.

To add the LED On endpoint, set the Request name to be “LEDOn”, the Method to be “GET”, and the URL to be “/led/on”. If you then click the Send button to test the request you should see the LED light up and the JSON data be returned. We can use this to create a response entity if we want.

We can add the LED Off endpoint by doing the same again, but this time changing the URL to be “/led/off”. Clicking Send this time will turn the LED off.

We can add the Temperature endpoint by doing the same again, but this time changing the URL to be “/temperature”. Clicking Send will now read the temperature and return it in the JSON response. We should create a new entity to store this by going into the Response Structure tab and clicking Create Entity. I called my entity “RootTemperature”.

Now we need to be able to call these endpoints from our application. We do this using Mendix microflows.

To turn the LED On, we can create a new microflow called ACT_LEDOn. Add an action, and look for Send REST Request. You should see the LEDOn call we created earlier, so select that. Finally make sure the microflow has a suitable User Role selected so it can be executed and save it.

Do the same for LED Off.

On a page, drag the ACT_LEDOn microflow somewhere suitable to create a button, and also do the same for ACT_LEDOff. I renamed mine to LEDOn and LEDOff.

Now run the application and open it in your browser.

You should get a page with the two buttons on. Pressing them should turn the LED on and off on the Raspberry Pi Pico W.

To read the temperature on the Pico, first create a new Page in Mendix called Temperature. I used a Popup layout.

Add a DataView to the page, and use the RootTemperature entity we created earlier. Let it automatically populate the page for you. I removed the Save and Cancel buttons and replaced them with a single Close page button.

Now create a new microflow called ACT_Temperature. Add a Send REST Request action, and call the Temperature endpoint. Use the Return Value, I called mine RootTemperature, and pass this as the parameter to a Show Page action that calls the Temperature Popup page we just created. Save the microflow, and remember to give it a suitable User Role. Drag the ACT_Temperature microflow onto the same page as the LED buttons to create a new button. I renamed mine to Temperature.

Run the app, and you should have three buttons. Click Temperature and you should get a popup with the current temperature according to the ADC on your Raspberry Pi Pico W.

Conclusion

Congratulations, you have successfully integrated Mendix with your very own Internet of Things (IoT) device!

Hopefully this article has shown how easy it is for a modern low code application to interact with a cheap microcontroller so it can interact with sensors and output devices.

We’ve also seen how to easy it is to integrate REST services with Mendix using the new Consumed REST Service functionality.

Getting the current language code in a Mendix app from JavaScript

When we’re working on a multi language application, we need to be aware of the user’s language ensure they receive content in their own language.

Mendix has great language support, and it also has great caching on the client side. These can cause problems together when you change the language of a user in your application, it may not always be reflected back to local cache.

There is a way around this, we can use the Mendix Client API to force a retrieve of the data from the application. We need to use an XPath request to ensure we bypass the cache.

let xPath = "//System.Language[System.User_Language=\"" + mx.session.getUserId() + "\"]";

We have the current user’s session ID, so we can retrieve the System.Language using this a constraint on the System.User_Language association.

Now we can just use an mx.data.get call to retrieve it.

mx.data.get({
    xpath: xPath,
    filter: {
        amount: 1
    }, 
    callback: function(obj) { 
        let lang = obj[0].jsonData.attributes.Code.value;
        console.log("Current language is " + lang);
    }
});

In this example, we are just echoing the language code back to the user on the console.

Logging from a Mendix Java action

How to add logging to a Mendix Java action.

It is a common requirement to be able to log data from an application. Mendix has a good logging system built in, but how do we access this from a custom Java action?

The Mendix development team have already thought of this, and have provided a method called getLogger in the Core.

To use the logger from a Java action, we first need to add a couple of imports.

import com.mendix.core.Core;
import com.mendix.logging.ILogNode;

Now we need to provide access to the logger. We can do this by providing a static variable which we’ll call LOG. We use the getLogger method we mentioned earlier, passing in the name of the log node name we want to use. In this case, we’ll use “RobTest” as the log node name.

// BEGIN EXTRA CODE
public static ILogNode LOG = Core.getLogger("RobTest");
// END EXTRA CODE

Now, this is in place, we can access LOG from elsewhere in our Java action.

Let’s say our action wants to log when it starts and also wants to list all the microflows available within the App. We can change the log levels, so the start notification is at “info” level, and the microflows are at “debug” level. We can do this using the following.

public java.lang.Boolean executeAction() throws Exception
{
    // BEGIN USER CODE
    LOG.info("Running executeAction()");

    for (String mf : Core.getMicroflowNames()) {
        LOG.debug("Microflow: " + mf);
    }
    return true;
    // END USER CODE
}

When we execute the Java action, we see the following on the Mendix console.

Debugging a Mendix widget

I had to debug a custom Mendix widget that was failing. All that was shown on screen in red was “Could not create widget BHCCDropZone.widget.BHCCDropZone”.

We’d recently upgraded to Mendix 7.18.1, so something looked like it happened during the upgrade.

The first step was to make sure the appropriate access rights to the entity the widget uses were right. In this case they were.

Next was to up the log levels from INFO to DEBUG in the Mendix Modeller to see if that gave me any clues. I could see the widget’s constructor and postCreate being run as I had logger.debug calls in those functions being run. However, one of my debug calls was not being reached, so I had narrowed down the location of problem.

After this I added JavaScript breakpoints in the failing method using Chrome’s developer tools. The widget is only loaded the first time the page is reached. When I reached this page I singled stepped through, and found an exception being thrown by the JavaScript but being caught by Mendix. My error was…

"TypeError: Converting circular structure to JSON
    at JSON.stringify ()
    at http://localhost:8080/mxclientsystem/mxui/mxui.js?636758982080189581:74:164930
    at Array.map ()
    at t.log (http://localhost:8080/mxclientsystem/mxui/mxui.js?636758982080189581:74:164840)
    at http://localhost:8080/mxclientsystem/mxui/mxui.js?636758982080189581:74:196250
    at http://localhost:8080/mxclientsystem/mxui/mxui.js?636758982080189581:74:166406
    at Array.forEach ()
    at t.log (http://localhost:8080/mxclientsystem/mxui/mxui.js?636758982080189581:74:166377)
    at t.debug (http://localhost:8080/mxclientsystem/mxui/mxui.js?636758982080189581:74:165718)
    at Object.postCreate (http://localhost:8080/widgets/BHCCDropZone/widget/BHCCDropZone.js?636758982080189581:80:11)"

I could now see I had a circular data structure.

Working backwards through the stack trace, I could see the problem was in my widget’s postCreate function, and it was a debug function causing the problem. This was the faulty code causing the widget to fail.

My postCreate function looked like this…

postCreate: function () {
  console.log("BHCCDropZone session", mx.session);
  logger.debug(this.id + ".postCreate");
  this.initBHCCDropZone();
  logger.debug("this", this);
},

The faulty line was this…

logger.debug("this", this);

Trying to send the contents of this to the debug logs was causing problems as it was a circular data structure. Removing this line of JavaScript fixed the problem, and the widget started to work again as expected.

I hope this helps others in future when debugging “Could not create widget” errors in Mendix.

Using a http proxy from a Mendix Java action

As part of some work I have been undertaking to integrate the UK Government Notifications service into Mendix, I needed to be able to make API calls from behind a firewall using a proxy in a Java action.

Due to the lower level Java actions in Mendix run at, proxy settings are not automatically applied, and must be added manually. I wanted to explain how to get the proxy settings from Mendix, and use them a Java action.

I’ve previously explained how to add proxy settings to Mendix, so I assume this step has been completed.

In a Java action, we need to get these from the HttpConfiguration singleton.

import com.mendix.http.HttpConfiguration;
import com.mendix.http.IHttpConfiguration;
import com.mendix.http.IProxyConfiguration;

IHttpConfiguration httpconf = com.mendix.http.HttpConfiguration.getInstance();
IProxyConfiguration proxyconf = httpconf.getProxyConfiguration().orElse(null);

We can now check if we have a proxy configuration set, if we don’t proxyconf will be null.

The username and password for the proxy can be retrieved using the getUser() and getPassword() methods.

String username = proxyconf.getUser().orElse(null);
String password = proxyconf.getPassword().orElse(null);

If they are present we can build a Java Authenticator object and set it as the default authenticator.

import java.net.Authenticator;
import java.net.PasswordAuthentication;

if (username != null && password != null) {
    Authenticator authenticator = new Authenticator() {
        public PasswordAuthentication getPasswordAuthentication() {
           return (new PasswordAuthentication(username, password.toCharArray()));
        }
    };

    Authenticator.setDefault(authenticator);
}

Next we need to create the Proxy object. We need to get the host and port of our proxy server from Mendix using the getHost() and getPort() methods.

import java.net.InetSocketAddress;
import java.net.Proxy;

InetSocketAddress proxyLocation = new InetSocketAddress(proxyconf.getHost(), proxyconf.getPort());
Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyLocation);

The proxy can be used for Java network actions.

An example of using this would be the UK Government Notifications client. It has a second optional paramater in it’s constructor for a Proxy.

client = new NotificationClient('APIKey', proxy);

Using a proxy server from the Mendix Modeller

There are times when building online services you find yourself behind a firewall and need to use a proxy. Sometimes these are transparent, but other times you need to add settings by hand.

In a Mendix app, an example may be when you need to consume a REST service from outside you home network.

To configure proxy settings in Mendix, you need to go to our Project’s “Settings”. Open “Configurations”, select your working configuration, and click “Edit”. Select the “Custom” tab and add the following “Names” and “Values”.

http.proxyHost The name your proxy
http.proxyPort The port your proxy is running off of.

If my proxy was running on proxy.robertprice.co.uk:8080, my settings would be

http.proxyHost proxy.robertprice.co.uk
http.proxyPort 8080

Sometimes the proxy will also need a username and password. You can set these using http.proxyUser and http.proxyPassword. For example

http.proxyUser RobertPrice
http.proxyPassword SecretPassword

You should now be able to access external services through the proxy from Mendix.

Example proxy settings for the Mendix Modeller

More information on using a proxy in Mendix is available at Using a proxy to call a REST service.

Extracting text in Mendix using RegexReplaceAll

It’s a fairly common requirement to be able to extract text from a larger string.

Using Mendix, the easiest way I’ve found to do this is using the RegexReplaceAll Java action from the Community Commons module.

We use a regular expression extract the text, then return this selected text in the action.

For example, take the following string returned from the Nexmo SMS module.

--------- part [ 1 ] ------------Status [ 9 ] ...SUBMISSION FAILED!Message-Id [ null ] ...Error-Text [ Quota Exceeded - rejected ] ...Message-Price [ 0.03330000 ] ...Remaining-Balance [ 0.03200000 ]

If we want to extract the Error-Text we can use the following regular expression.

^.*Error-Text \[ (.*?) \].*$

Here we’re saying look for the text between the square brackets after the string Error-Text. We use round brackets to say we want to remember this matched text. We can then use the regular expression match position to return the matched text – in this case $1.

If we run this over our string we get the following

Quota Exceeded - rejected

To use this in a Mendix microflow, assume we have our status message in a String $StatusMessage that we pass into the Java action. This is our Haystack.

Next, we use the regular expression as a String for our Needle regex.

Finally, we say we want $1 as a String to be our Replacement.

We return the String as $Details.

This is what the microflow and action should look like.

View Robert Price’s Mendix Profile.