RC2024 – Part 7 – Reading The Rotary Encoder Using Z80 Assembly Language

Previously as part of this year’s Retro Challenge, I learnt how to build and run Z80 assembly language programs on my RC2014 Classic 2 computer.

I now want to recreate the BASIC program that moves LEDs using Z80 Assembly Language.

I am using IO address $DE for my Rotary Encoder module. I also have the Digital I/O module using IO address $03. I can define these as constants so I can easily change them if necessary.

I need to store values for the input, the output, and the last value of the CLK pin. The input will just be the value from the IN operation. The output value will be the byte we want to show on the LED output. I will set this to be %00000001 initially. When I turn the encoder, I want this to shift to either the left or right. If it reaches the edge, I want it to wrap. The Z80 operations RLCA and RRCA will do this for me.

To check if a bit is high or low, I can use an AND operation to mask out out other values. For example, to check if the SW1 is being pressed, I can do the following.

    SW1         EQU %00000100
    INPUT_PORT  EQU $DE

    in a,(INPUT_PORT)
    and SW1
    cp SW1
    jp z, end    ; jump to end if SW is high.

I can repeat this logic to check the values of CLK1 and DT1.

This is the code I have come up with.

    OUTPUT rotaryencoderledcontrol.z80

; On the RC2014 Classic 2 running from BASIC the Z80
; code runs from address $9000. 
    ORG $9000

; The input and output ports to use.
INPUT_PORT  EQU $DE
OUTPUT_PORT EQU $03

; The input bits from the rotary encoder.
CLK1    EQU %00000001
DT1     EQU %00000010
SW1     EQU %00000100

; show the inital led value
    ld a,(output)
    out (OUTPUT_PORT),a

loop:
; load the last clk value into register b
    ld a,(lastclk)
    ld b,a

; read the input port and store in "input"
    in a,(INPUT_PORT)
    ld (input),a

; now check if the switch on first rotary encode has been
; pressed. If it has jump to end
    and SW1
    cp SW1
    jp z, end

; now see if clk1 matches the lastclk. If it does loop
    ld a,(input)
    and CLK1
    ld (lastclk),a
    cp b
    jp z, loop

; now work out what direction we are moving
check_case1:
    ld  a,(input)          
    and CLK1
    cp  CLK1             
    jr  nz, check_case2

    ld  a,(input)         
    and DT1
    cp  DT1
    jr  z, left     ; if both CLK and DT are high then left

check_case2:
    ld  a, (input)
    and CLK1
    cp  0 
    jr  nz, right

    ld  a, (input)
    and DT1
    cp  0 
    jr  nz, right  

; we must be turning left so rotate the output to the left
; and store it before going back to the start of the loop.
left:
    ld a,(output)
    rlca
    out (OUTPUT_PORT),a
    ld (output),a

    jp loop

; we must be turning right so rotate the output to the right
; and store it before going back to the start of the loop.
right:
    ld a,(output)
    rrca
    out (OUTPUT_PORT),a
    ld (output),a

    jp loop

; the switch has been pressed, so we clear the output
; and exit.
end:
    ld a,0
    out (OUTPUT_PORT),a

    ret

input:
    db  0
output:
    db  %00000001
lastclk:
    db  0

The LED successfully moves left and right depending on how I turn the rotary encoder. However, because I am checking in both high and low states of CLK1, it is moving two steps per turn. This will be too senstive to use in an application, so my next job is to change this to check once per turn.

RC2024 – Part 6 – Getting Z80 Assembly Language Programs On To The RC2014 Classic 2

As part of this year’s Retro Challenge, I am building a rotary encoder module for the RC2014 computer.

I have built a custom PCB, and I can use it from BASIC. However, I would also like to be able to use it from Z80 machine code.

To run Z80 machine code on my RC2014 Classic 2 I have a few options.

  1. Use BASIC to load in a hex dump of assembled code.
  2. Use the SCM ROM image to load in a hex dump of assembled code.
  3. Burn my assembled code into a ROM and insert that into the RC2014.

I have designed a new ROM PCB to help me do options 2 and 3 in the future. For now, I will use BASIC to load in assembled code and run it.

Before I can load assembled code, I need to write and assemble it.

I am going to do this on an Apple Macbook Pro. I’m going to need an assembler, and something to create hex dumps from the assembled code.

I am going to use SJASMPLUS as my Z80 assembler. On a Mac this needs to be built from the source code. In a terminal window the following should work.

make clean
make
sudo make install

To create the hex files, I am going to use the z88dk-appmake command from z88dk. z88dk is also provides an assembler and a C compiler that can build applications for the RC2014. I’m not going to use these at the moment. There are installation instructions and a binary that can easily be installed on a Mac.

You can use any text editor you want, but I’m going to be using Visual Studio Code. I’m also using the Z80 Assembly extension for syntax highlighting.

I’m going to write a simple Z80 assembly language program to read the the input from the rotary encoder and show it on the Digital I/O module’s LEDs. It’s going to exit when the rotary encoder’s switch is pressed.

The Rotary Encoder module is on input address $DE. The Digital I/O module is on input address $03.

    OUTPUT rotaryencodertest.z80

; On the RC2014 Classic 2 running from BASIC the Z80
; code runs from address $9000. 
    ORG $9000

; The input and output ports to use.
INPUT_PORT  EQU $DE
OUTPUT_PORT EQU $03

; The input bits from the rotary encoder.
CLK1    EQU $1
DT1     EQU $2
SW1     EQU $4


loop:
; read the input port
    in a,(INPUT_PORT)
; send the input directly to the output port
    out (OUTPUT_PORT),a

; now check if the switch on first rotary encode has been
; pressed. If it hasn't, loop back.
    and SW1
    cp SW1
    jp nz, loop

; the switch has been pressed, so we clear the output
; and exit.
    ld a,0
    out (OUTPUT_PORT),a

    ret

I’ve saved this as rotaryencodertest.s.

To assemble to code I need to use the following line in a terminal…

sjasmplus rotaryencodertest.s

To convert the output to intel format hex, I need to use the following line in a terminal…

z88dk-appmake +hex --org 0x9000 -b rotaryencodertest.z80

I should now I have a file called rotaryencodertest.ihx.

To load this onto the RC2014 Classic 2, I can use the example hexload.bas program. I’ll include the full code here.

new
clear
10 REM Created by Filippo Bergamasco,
11 REM and modified by DaveP for the RC2014
12 REM Adapted for z88dk by feilipu
20 REM Version 1.0
30 Print "Loading Data"
40 let mb=&H8900
50 print "Start Address: ";hex$(mb)
60 REM Go to READ Subroutine.
70 GOSUB 1000
80 print "End Address:   ";hex$(mb-1)

90 REM Change USR(0) Pointer for HexLoad
100 GOSUB 1100

110 REM RUN THE HEXLOAD CODE!
120 print usr(0)

130 REM Change USR(0) Pointer to 0x9000
140 GOSUB 1200

150 REM RUN THE PROGRAMME CODE!
160 print usr(0)
170 END 

1000 REM Routine to load Data
1010 REM Needs var mb set to start location
1020 read a
1030 if a>255 then RETURN
1040 rem print HEX$(mb),a
1050 poke mb, a
1060 let mb=mb+1
1070 goto 1020

1100 REM Location of usr address &H8049
1110 print "USR(0) -> HexLoad"
1120 let mb=&H8049 
1130 doke mb, &H8900
1140 RETURN 

1200 REM Location of usr address &H8049
1210 print "USR(0) -> 0x9000, z88dk default"
1220 let mb=&H8049
1230 doke mb, &H9000
1240 RETURN

9010 data 33,116,137,205,109,137,215,254,58,32,251,14
9040 data 0,205,83,137,71,205,83,137,87,205,83,137
9070 data 95,205,83,137,254,1,40,23,254,0,32,33
9100 data 205,83,137,18,19,16,249,205,83,137,121,183
9130 data 32,26,62,35,207,24,207,205,83,137,121,183
9160 data 32,14,33,206,137,205,109,137,201,33,172,137
9190 data 205,109,137,201,33,189,137,205,109,137,201,205
9220 data 100,137,7,7,7,7,111,205,100,137,181,111
9250 data 129,79,125,201,215,214,48,254,10,216,214,7
9280 data 201,126,183,200,207,35,24,249,72,69,88,32
9310 data 76,79,65,68,69,82,32,98,121,32,70,105
9340 data 108,105,112,112,111,32,66,101,114,103,97,109
9370 data 97,115,99,111,32,38,32,102,101,105,108,105
9400 data 112,117,32,102,111,114,32,122,56,56,100,107
9430 data 10,13,58,0,10,13,73,110,118,97,108,105
9460 data 100,32,84,121,112,101,10,13,0,10,13,66
9490 data 97,100,32,67,104,101,99,107,115,117,109,10
9520 data 13,0,10,13,68,111,110,101,10,13,0,0
9550 data 999
9999 END
run

Paste this into a terminal connected to the RC2014 and it will prompt you to enter hex. Cut and paste the rotaryencodertext.ihx and it should load and execute.

Now turning the rotary encoder will show the binary input on the output LEDs. Pressing the switch on the rotary encoder attached to port 1 will return you to BASIC.

This is it running. Ignore the poor soldering on the Digital I/O board. I did that a few years ago and (I think) I have improved since then.

RC2024 – Part 5 – Building And Testing The PCB

My prototype PCBs from JLCPCB has arrived!

Rotary Encoder for RC2014

The parts I needed to solder onto the board have also arrived from AliExpress and CPC Farnell.

I found one very annoying mistake try to solder up the board. I had forgotten to add a sensible spoke width to my copper ground plane. This means the ground plane is soaking up the heat from my soldering iron anytime I try to solder any pin connected to ground. I was able to solder up the board, but those pins took between 10 and 20 seconds to solder. I’m not confident the connection will last.

In the next iteration of the board I will set the spoke width in the EasyEDA CopperArea properties to 0.3mm. This should still give a good ground connection, but also be easy to solder.

I’m not happy with the position of some of the text on the board. Once wired up, pins obscure the Port 1 and Port 2 text. The debug port is also obscured. I will move these so they are visible when connected.

The debug port is a bit too high so the pins are clear of the top of the board. I will move them down.

I also realised that Spencer calls all his PCBs “modules”, so I will call mine “Rotary Encoder Module”.

Rotary Encoder for RC2014

It was with great excitement I plugged the module into my RC2014 and turned it on. The LEDs for Port 1 were all off, but the LEDs for Port 2 were all on. That didn’t seem right, but then I realised I had only plugged a rotary encoder into Port 1. As Port 2 is returning logic 0, the 74HCT14 was inverting this to logic 1 so the LEDs lit. So it was working as expected. Adding another rotary encoder to Port 2 fixed that.

The address was set to 0 on the address switches. This meant I could use the same BASIC program I used before to test the rotary encoders.

Turning the encoders the LEDs were flashing in the expected order. The BASIC program was returning “Left” and “Right” depending how I turned the encoder. It was working!

I decided to test the address switches and so set it them to 01111011, which is hex DE, and decimal 222.

I modified the BASIC program to read address DE instead of 0.

30 LET IN = INP(&HDE)

Running the BASIC program, the module now responds to address DE as expected.

One final test was to plug in my RC2014 Digital IO module. This is set to address 3. I modified the BASIC program so the OUT statement used address 3.

40 OUT 3,2^COUNTER

I can now use my rotary encoder module to successfully move the LEDs on the Digital I/O module.

Despite the issues with soldering to the ground plane, my module works as I hoped. I am going to tweak the layout and order replacement PCBs from JLCPCB.

RC2024 – Part 4 – A Quick Diversion To Look At The RC2014 ROM Board

I have been thinking about what else I want to achieve with this year’s Retro Challenge.

Apart from designing a PCB to interface rotary encoders to my RC2014 computer, I wanted to be able to use them from software. I have used BASIC for my testing so far, but I also want to look at using Z80 assembly language.

To use Z80 assembly language on my RC2014 Classic 2, I have a few options.

  1. Use BASIC to load and execute a hex dump of the my assembled Z80 code.
  2. Use SCM to load and execute a hex dump of my assembled Z80 code.
  3. Burn my assembled code into a ROM and run that directly on the RC2014.

The ROM board for the RC2014 Classic 2 has the ROM chip socketed. If I wanted to use my own ROM image I can lift this out and replace it. However, the board is low down and I would need to also remove the board from the RC2014 backplane. This would add mechanical wear and tear.

I have a similar problem if I want to switch between ROM images. For example, from BASIC to SCM, or to my own custom ROM image. There are jumpers on the ROM board, but I would need to lift the board from the backplane to be able to swap them.

So, what is the solution?

Well, Spencer does have an RC2014 riser card available on the Z80 Kits website to lift the board above the other cards. This makes it possible to reach the jumpers, but still difficult to remove and replace the ROM.

After writing about designing the PCB for the rotary encoder, I thought I could just build my own ROM card. What I want my new ROM board to achieve is…

  • Use a ZIF (zero insertion force) socket for the ROM so I can easily remove and replace it.
  • Use switches instead of jumpers for the ROM select so I can easily change which part of the ROM the RC2014 is seeing.
  • Have the ZIF and switches above the other boards so I can easily access them.

The circuit design of the ROM board is essentially the same as the original ROM board, just with some pull down resistors near the address switches. The board height will be the same as a standard RC2014 board.

I couldn’t find a DIP package with 3 switches in the EasyEDA library, so I have used a 6 pin socket in the design which should have the same footprint.

Here is my PCB layout.

I have sent this off to JLCPCB to be manufactured. The cost including delivery was just £2.53 using their 10 to 14 day delivery option. I’m not in a rush for this board, so I think that is a fantastic price. There is also a $2 discount at the moment, so that also reduced the cost.

I am expecting the board to arrive towards the end of the month but this won’t stop work elsewhere in the project.

RC2024 – Part 3 – Designing A PCB For The Rotary Encoder

One thing I really wanted to do as part of this year’s Retro Challenge was to build my first custom PCB for the RC2014 computer.

I have chosen to use EasyEDA to design my PCB. I was heavily influenced by watching James Sharman’s excellent YouTube videos in which he designs his own CPU using EasyEDA. The alternative was KiCad, which seems more complex for a beginner.

Spencer has kindly put the specifications for a RC2014 module template up on the RC2014 website. I will try to follow this when it comes to laying out my PCB.

The Circuit

The RC2014 Peripheral Addressing didn’t originally target exact addresses, instead it used 74LS138 chips to target ranges of addresses.

The SC219 takes a different approach, and uses a 74HCT688 8-Bit Magnitude Comparator to target a specific address. I think this is the best approach for my circuit. Using switches for the comparison will mean I can easily change the matched address if there are conflicts with any other cards on my RC2014. I will use the IORQ line from the RC2014 as the OE (Output Enable) line. I can take the output from the 74HCT688 and use a 74HCT32 OR Gate with the RD line to know when the peripheral address I want to respond to is being used. I will use a 74HCT245 Octal Bus Transceiver to present data on the data pins when active.

In my initial post I mentioned I had to use a 74HCT14 Schmitt Trigger to invert the input from the rotary encoder and help produce a cleaner digital signal. This will be connected to the 74HCT245’s D0 to D5 pins. This will allow me to support two rotary encoders, and leave D6 and D7 spare. I will provide two blocks of input pins, one block for each rotary encoder. Each of these will support the CLK, DT, and SW pins, as well as GND and +5V.

I will add some debugging support to the circuit. I like the input LEDs on the SC219 so I will also add these to my circuit so I can see the incoming data from the rotary encoders. I think a breakout of the incoming data lines before the 74HCT245 will be useful to see what is going on, and also allow the board to be used as an input device when the rotary encoders aren’t being used. Finally, I saw a discussion on one of the Sinclair Spectrum groups about adding a hook to GND to attach an oscilliscope to when debugging. This could be very useful if the circuit doesn’t work, so I will add in provision for this.

This is my initial design.

Rotary Encoder circuit

The PCB

As I mentioned earlier, there is a specification for the shape and size of PCBs for the RC2014.

Laying out the board I have tried to use EasyEDA’s measuring tool to ensure this is correct.

Adding the curved corner I found tricky, but eventually I was able to do this by targetting the BoardOutLine layer in EasyEDA.

I read on various forums that adding a ground plane to a PCB was a good idea, so I have done this on both the top and bottom layer of my PCB.

As my circuit is reasonably simple, I used EasyEDA’s auto routing functionality. This worked quickly and I didn’t need to do any manual routing.

For manufacturing I decided to use JLCPCB as EasyEDA has built in support for this manufacturer.

The board itself cost $3.20 for 5. I paid for express delivery because if I have made a mistake and need to create a new revision, it could take several weeks to arrive. The total for the 5 PCBs, delivery, and tax was £20.21. These are due for delivery on October 8th 2024.

JLCPCB adds a manufacturing code to each PCB. You can specify where this appears by adding the text “JLCJLCJLCJLC” to one of the silk layers. I put mine on the BottomSilkLayer so it’s not forward facing.

This is the final layout for my board. I can’t wait for it to arrive so I can solder it up and see if it works.

Rotary Encoder PCB

RC2024 – Part 2 – Reading The Rotary Encoder From BASIC

My Retro Challenge this year is to get rotary encoders working on my RC2014 computer.

Previously, I explained what a rotary encoder was, how to use one, and how to link it to the RC2014.

I now want to try reading it on the RC2014 and using it to control some output.

I’m using the SC219 Digital I/O Board on the RC2014 to accept inputs from a rotary encoder. I have mine mapped to I/O address port 0. Using Microsoft BASIC I can read this using the INP(0) statement. This will return a byte representing the values of D0 to D7 on the input port.

I am using D0 for the CLK, D1 for DT, and D2 for SW. D3 to D7 are not used at the moment. I am only interested in D0 and D1 for the rotary motion. I can use AND statements to mask bits when I’m checking the input value. So to check if D0 was 1 or 0, I could use INP(0) AND 1. This would return either 0 or 1. To check D1, I could use INP(0) AND 2. This would return either 0 or 2.

The following UML activity diagram shows at a high level how I need my BASIC program to operate.

The Microsoft BASIC on the RC2014 is old and limited compared to modern languages, but it is functional.

To save space, I’ll use variable A for CLK, and B for DT. I’ll need to keep track of the last state of A so I know when it has changed. To do this I’ll create a variable LAST_A. Finally, to keep track of the INP(0) result, I’ll create variable INPUT.

When I detect a change in A, I need to look to see if B matches A. If it does, then I know the encoder is being turned to the right. If it doesn’t, then it is being turned to the left.

Here is the BASIC code I came up with to do this. When it detects a change, it prints either “Left” or “Right” to the console.

10 LET LAST_A = 0
20 LET IN = INP(0)
30 LET A = IN AND 1
40 IF A = LASTA THEN GOTO 200
50 LET B = IN AND 2
60 IF (A=1 AND B=2) OR (B=0 AND A=0) THEN GOTO 100
80 PRINT "Right"
90 GOTO 200
100 PRINT "Left"
200 LET LAST_A = A
210 GOTO 20

The SC219 has digital outputs as well as inputs. Using the built-in output LEDs, I can move a dot left or right depending on the input from the rotary encoder.

The 8 output LEDs represent each bit of a byte. So we have the values 1, 2, 4, 8, 16, 32, 64, and 128 represented on bits D0 to D7. This is 2 to the power X, where X is between 0 and 7. We can use the ^ (power) operator in BASIC to help set these bits.

So we need to have a variable, let’s call it COUNTER, to keep track of which power we are currently at. We can say turning the rotary encoder left will increment COUNTER by 1. Turning the rotary encoderrigtht will decrement COUNTER by 1. We need some guards, so if COUNTER goes below 0, we set it to 7. If it goes above 7, we set it to 0. We set one of the LEDs by using OUT 0,2^COUNTER. This means when we turn the rotary encoder, we can move the LED.

Here is the BASIC code I wrote to do this.

10 LET COUNTER = 0
20 LET LAST_A = 0
30 LET IN = INP(0)
40 OUT 0,2^COUNTER
50 LET A = IN AND 1
60 IF A = LASTA THEN GOTO 200
70 LET B = IN AND 2
80 IF (A=1 AND B=2) OR (B=0 AND A=0) THEN GOTO 150
90 IF COUNTER <= 0 THEN GOTO 120
100 LET COUNTER = COUNTER - 1
110 GOTO 130
120 LET COUNTER = 7
130 PRINT "Right"
140 GOTO 200
150 IF COUNTER >= 7 THEN GOTO 180
160 LET COUNTER = COUNTER + 1
170 GOTO 190
180 LET COUNTER = 0
190 PRINT "Left"
200 LET LAST_A = A
210 GOTO 30

Here is a short video of it in action.

RC2024 – Part 1 – The Rotary Encoder

It’s October 2024, and Retro Challenge RC2024/10 has started.

I’m looking at interfacing rotary encoders with the RC2014 computer this year.

What is a rotary encoder?

A rotary encoder is an electronic device that measures rotational movement.

A rotary encoder

The rotary encoder I’m using generates two pulses as it turns. The pulses are on pins CLK (Clock) and DT (Direction of Travel). These pulses are out of phase with each other and this tells us which direction the encoder is turning. The encoder starts with both pins at logic 1. We then look for when CLK transitions to logic 0. At this point we check DT to see if it’s logic 1 or logic 0. This will tell us which way the encoder is being turned.

Let’s look at this on a timing diagram.

Timing diagram for a rotary encoder.

Going from A to B shows us turning the rotary encoder clockwise. We see CLK going from logic 1 to logic 0. We then sample DT and see it is still logic 1, so we know we are turning clockwise.

Going from B to A shows us turning the encoder anticlockwise. We see CLK going from logic 1 to logic 0. We then sample DT and see it is logic 0, so we know we are turning anticlockwise.

You may have noticed my rotary encoder has another pin call SW.

This is a switch triggered by pressing the shaft of the rotary encoder down. It is usually logic 1, but is logic 0 when pressed.

Wiring the rotary encoder to the RC2014

I need a way to be able to read the the CLK, DT, and SW pins.

For prototyping, I am using Stephen Cousins SC129 Digital I/O board for the RC2014. This has 8 input and 8 output pins, as well as LED indicators. I can use this from MS-BASIC on my RC2014 Classic 2.

The output from the rotary encoder could be noisy. Because of this, I am going to use a 74HCT14 Inverting Schmitt Trigger to clean the signals, and also invert them. Inverting the signals means they appear at logic 0 at rest. I will need to look for CLK transitioning to logic 1 when turning.

Wiring up the rotary encoder and turning it, I can see the input LEDs for 1 and 2 flashing. This is showing the logic levels changing from 0 to 1 and back again. I can also see they are out of sync with each other as expected.

My next step will be to read the incoming data from the rotary encoder in software.

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.

Taking part in Retro Challenge 2024

It’s the 20th anniversary of the Retro Challenge, and also the 10th anniversary of the RC2014 Z80 computer.

So to celebrate, I’m planning on taking part this year, and undertaking a project with my own RC2014 computer. The idea is to complete a project using a retro computer in a month. The RC2014 itself was an entry in 2014.

As I will only have a month to try to complete a project, I’m looking for something achievable.

My plan at the moment is to look at adding rotary encoders to my RC2014, and to be able to read them using Z80 machine code. A rotary encoder is a sensor that measures the rotational position and speed of an object by converting the motion into an electrical signal. An example would be the scroll wheel on a computer mouse.

I have ordered a couple of rotary encoders ahead of the start date so they should be here in time for the start of the project. I have used a rotary encoder on an Arduino a few years ago, but that had a library to simplify the process. This time I will have to write from scratch, and it will give me a good opportunity to write some real Z80 assembly.

I will also need to design a circuit board, and for this I plan to use EasyEDA. This is an area I really need to learn more about, so the challenge gives me a good excuse to get stuck in with it. I will need to start on this early as ordering a PCB can take a few weeks to arrive, so I won’t have time for multiple revisions if I make a mistake.

Let’s see if I can produce something by the end of the project!

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.