Microbit V1 display driver

The image above shows a Microbit V1 connected to an ST7789 1.14′ display via a Kitronik breakout board. The SPI interface on the Microbit (NRF51822) is a little slow at 4MHz but it is ok for simple outputs as shown above. Code was developed using VSCode, PlatformIO the mbed framework. The full list of connections can be found in the file display.cpp which is available on the github repo over here

An NRF52832 with an ILI9341 display

The NRF52832 is a Bluetooth microcontroller from Nordic Semiconductor. Boards and modules based on it are available from a number of different suppliers. I got a few on Aliexpress that bear the Ebyte logo. These were pre-programmed (and locked) with a software image from Ebyte. As I planned writing my own code I had to first unlock the device. Normally I work with an STLink debugger (clone) but this is apparently not able to unlock the NRF52832. Fortunately I had previously bought a J-Link Edu probe and this able to unlock the module.

The next hurdle was to figure out which pin is which as the board labels are specific to some other application. With a bit of fishing around datasheets and poking with a multimeter I came up with this:

The pin numbers could now be related to port numbers in the NRF52832.

Next the device had to be connected up to the ILI9341 display module. This too was bought on AliExpress. It is an SPI device that includes a touch screen controller. The wiring to the NRF52832 module is as follows:

ili9341NRF52832Remarks
T_IRQP0_40 if screen is touched
T_DOP0_26MISO
T_DINP0_25MOSI
T_CSP0_5Chip select for touch interface (active low)
T_CLKP0_27SPI clock
SDO (MISO)N/C
LEDVia 10 ohm resistor to ground
SCKP0_27SPI clock
SDI (MOSI)P0_25MOSI
D/CP0_6Switches between data and command mode (0=Command mode)
RESETP0_7Clear to 0 to reset the display
CSP0_28Chip select for display (active low)
GND0V
VCC3.3V

MBed and Visual Studio Code / PlatformIO were used to write code for the device. Initially Mbed was used but it can be slow at certain times of the day so I switched to VSC/PlatformIO for local development. This was a little fiddly to set up but it worked out very well in the end. MBed OS was used as a basis for the code and a working demo is available over here.

Mbed does not have a board support library for this specific board however the DELTA DFBM-NQ620 board uses the same NRF52832 module and works fine.

There were a couple issues along the way:

The NRF52832 has a maximum SPI speed of 8MHz which is a little slow for a display like this.

The Touch screen controller does not work well with the 8MHz SPI clock rate so this is switched down to 2MHz temporarily when reading the touch interface.

The rats-nest final system is shown below.

The STM32F411 “Black Pill”

The STM32F411 “Black Pill” board is a step up in performance from the Blue Pill (STM32F103). It contains a 100MHz ARM Cortex M4F CPU with 512kB Flash and 128kB of RAM. The usual sort of peripherals are also included (ADC, Timers, SPI, I2C etc.).

The Black Pill board comes with a set of pads on the underside that can accommodate an SPI flash chip. I was interested in giving this a go so soldered in an ST Micro M25P16 device. This has 2MB of storage that is page programmable (256 Byte pages) and sector erasable (64kB sector size). It also supports a bulk erase function.

There were no great problems getting a driver going except for one thing: When NSS is released (sent high) by the SPI peripheral I found that I had to introduce a short delay in the SPI driver. Without this delay, the interface to the flash chip did not work when one transaction immediately followed another. This may be because the pulse never left the STM32F411, or because of bad wiring, or because the flash chip just needs a moment (the datasheet seems to suggest at lease 100ns of a pause). Anyway, it now works and the image below shows a data read transaction captured using pulseview (Thanks pulseview authors! 🙂 ).

You can just make out the narrow NSS pulses (approx 400ns) that were necessary for the system to work. This image was captured with the SPI bus running at a reduced speed to allow for the limited bandwidth of my logic analyzer.

What could this be used for? Well, a data logger perhaps, or a store for various image assets for a screen or maybe even some sounds. If you use this be mindful of the write cycle limitation of these devices and especially don’t put a write in a fast loop.

Code for this and other examples can be found over on github : https://github.com/fduignan/stm32f411

Green power

The operators of the national grid in Ireland provide information about the fuel mix used for electrical power generation. This is updated every 15 minutes and can be seen here http://www.eirgridgroup.com/how-the-grid-works/system-information/

Delving into the source for this page a little it is possible to locate the web resource for the fuel mix which, it turns out, returns data in JSON.

So, a little bit of building later….

ESP32 with a PL9823 LED

Followed by a little bit of coding:

#include <ArduinoJson.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <SPI.h>
const char* ssid = "XXXXXXXXX";
const char* password =  "XXXXXXXXXX";
StaticJsonDocument<1024> doc; 
#define COAL 0
#define GAS 1
#define IMPORT 2
#define OTHER 3
#define RENEW 4
float coal, gas, import, other, renew;
float domestic;

void setup() {
  SPI.begin(1, 4, 2, 3); // (int8_t sck, int8_t miso, int8_t mosi, int8_t ss)
  Serial.begin(115200);
  
  delay(1000);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    Serial.println("Connecting to Wifi");
    writePL9823(0x00);
    delay(1000);
    writePL9823(0xffff00);
    delay(1000);    
  }
  Serial.println("Wifi ready");
  
}

void loop() {
  // put your main code here, to run repeatedly:
  if ((WiFi.status() == WL_CONNECTED)) { //Check the current connection status

    HTTPClient http;

    // Interesting discovery : setting the date/time to a future data returns the latest value
    http.begin("http://smartgriddashboard.eirgrid.com/DashboardService.svc/data?area=fuelmix&region=ALL&datefrom=17-Dec-2050+23:59&dateto=17-Dec-2050+23:59"); //Specify the URL
    int httpCode = http.GET();                                        //Make the request

    if (httpCode > 0) { // Http error?

      String payload = http.getString();
      Serial.println(httpCode);
      Serial.println(payload);
      deserializeJson(doc, payload);
      coal = doc["Rows"][COAL]["Value"];
      gas = doc["Rows"][GAS]["Value"];
      import = doc["Rows"][IMPORT]["Value"];
      other = doc["Rows"][OTHER]["Value"];
      renew = doc["Rows"][RENEW]["Value"];
      domestic = coal + gas + other + renew;
      Serial.print("Total (without export)= ");
      Serial.println(domestic);
      Serial.print("Renew = ");
      Serial.println(renew);
      Serial.print("% = ");
      Serial.println(renew / domestic);
      if ( (renew / domestic) > 0.5)
      {
        writePL9823(0x00ff00);
      }
      else
      {
        writePL9823(0xff0000);
      }
      delay(15 * 60 * 1000); // wait 15 minutes as that is the update interval
    }
    else {
      Serial.print("HTTP Error ");
      Serial.println(httpCode);
      writePL9823(0xff);
      delay(10000);
    }
    http.end(); //Free the resources
  }
}

void writePL9823(uint32_t Colour)
{
  // each colour bit should map to 4 SPI bits.
  // Format of Colour (bytes) 00RRGGBB
  uint8_t SPI_Output[12];
  int SrcIndex = 0;
  int DestIndex = 0;
  for (DestIndex = 0; DestIndex < 12; DestIndex++)
  {
    if (Colour & (1 << 23))
    {
      SPI_Output[DestIndex] = 0xe0;
    }
    else
    {
      SPI_Output[DestIndex] = 0x80;
    }
    Colour = Colour << 1;
    if (Colour & (1 << 23))
    {
      SPI_Output[DestIndex] |= 0xe;
    }
    else
    {
      SPI_Output[DestIndex] |= 0x8;
    }
    Colour = Colour << 1;
  }
  SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0));
  SPI.transfer(SPI_Output, 12);
  delay(10);
  SPI.endTransaction();
}

And hey presto!

A traffic light which is green when renewable power on the grid is greater than 50%, red otherwise. This could inform you when is a good time to turn on the clothes dryer for example.

I wouldn’t recommend that this be used to control devices directly as there are many ways it could be hacked.

The SAML10E16A: A Cortex-M23 MCU

SAML10E16A and a Segger J-Link EDU debug probe

This is my first Cortex M23 MCU. It is made by Atmel/Microchip which may lead to I/O synchronization oddness later but time will tell. Initial attempts to get this working with an ST-Link clone were not successful however the J-Link Edu works great.

SVD data was obtained from Atmel over at http://packs.download.atmel.com/

When you extract the SAML10E zip file you will come across this file: ATSAML10E16A.svd. This can be used to produce a header file with I/O structures using the utility svdconv.exe (available from Keil’s MDK installation). This is done as follows:

SVDConv.exe ATSAML10E16A.svd –generate=header

On my Linux system I added “wine” and the full path in front of that – you may not have to do this on your own system.

This produces a header file although not without some errors. The biggest problem seems to be in the area of the RTC. A little bit of manual editing fixed the “show-stopping” error in the generated header file. More investigation needed here however as other problems may lurk. Anyway, the header file was good enough to get a basic blinky program off the ground.

Blinky sources can be found over here https://github.com/fduignan/atmel/. I expect to add more in the near future.

Dublin Maker 2020+ badge.

Dublin Maker didn’t happen this year because of Covid-19. I had been working on a badge (unofficial) but then parked the project for a while. Recently I resurrected it and have begun building badges. Software is not complete yet and the documentation needs to be developed but the picture above shows an almost complete badge without the display fitted (it overlays the MCU in the middle of the badge). Just about all of the background hardware driver code is done.

The 2019 badge took a hardwired wired approach to multi-player gaming. While this worked, it was a little unreliable mainly due to low quality 3.5mm stereo sockets and cables. This badge uses an NRF24L01 radio module instead and it appears to work quite well. Also it is actually a little cheaper than the wired version. Other differences include a change to the MCU which features more pins and double the flash memory (32kB!!). The most notable change however is probably the use of a PCB. This was designed in KiCad and fabricated by PCBWay – a process that was surprisingly cheap, quick and easy.

Lets hope Dublin Maker 2021 takes place.

Using the HD107S RGB Led with a BBC Microbit

The HD107S is a low cost RGB LED unit which features an SPI interface. I paid just over €9 for 50 of them. They are quite easy to program using a microcontroller platform such as the BBC Microbit. The short program below was written on MBed and downloaded to the Microbit. It causes the LED to cycle through a range of colours.

#include "mbed.h"

SPI spi(P0_21, P0_22, P0_23); // mosi, miso, sclk
void getRainbow(unsigned &Red, unsigned &Green, unsigned &Blue);

int main() {

    unsigned Red,Green,Blue;
    Red = Green = Blue = 0;    
    char tx_buffer[16];
    char rx_buffer[16];
    spi.format(8, 3);
    spi.frequency(1000000);
    tx_buffer[0]=0; // header
    tx_buffer[1]=0; // header
    tx_buffer[2]=0; // header
    tx_buffer[3]=0; // header
    tx_buffer[4]=0xe0 + 0x1f; // max brightness (1f = brightness figure - lower to suit)
    while(1) {
        getRainbow(Red,Green,Blue);
        tx_buffer[5]=Blue; // blue
        tx_buffer[6]=Green; // green
        tx_buffer[7]=Red; // red        
        spi.write(tx_buffer, 8, rx_buffer, 0);              
        wait(0.01);
        
        
    }
}
void getRainbow(unsigned &Red, unsigned &Green, unsigned &Blue)
{   // Cycle through the colours of the rainbow (non-uniform brightness however)
    // Inspired by : http://academe.co.uk/2012/04/arduino-cycling-through-colours-of-the-rainbow/    
    static int State = 0;
    switch (State)
    {
        case 0:{
            Green++;
            if (Green == 255)
                State = 1;
            break;
        }
        case 1:{
            Red++;
            if (Red == 255)
                State = 2;
            break;
        }
        case 2:{
            Blue++;
            if (Blue == 255)
                State = 3;          
            break;
        }
        case 3:{
            Green--;
            if (Green == 0)
                State = 4;
            break;
        }
        case 4:{
            Red--;
            if (Red == 0)
                State = 5;
            break;
        }
        case 5:{
            Blue --;
            if (Blue == 0)
                State = 0;
            break;
        }       
    }    
}

A bluetooth low energy UV sensor

This is my first project that involves the NRF52832 BLE MCU. The NRF52832 takes the analog signal from an ML8511 UV sensor and makes it available over a BLE service. Connections are pretty straightforward: the ML8511 output is connected to P0_28 of the NRF52832. An LED is connected to P0_25. That’s all there is to it apart from power (3.3V) and ground. The BLE value output is an integer value that represents UV intensity in mW/cm^2.

Code was developed using the mbed online compiler. This particular BLE board was bought from AliExpress for around €6. It is not available as a “platform” on mbed however the Delta DFBM-NQ620 seems to be compatible so I used that. Interesting to note that at this point in time the Serial interface class for the NRF52832 is in a known broken state.

Code is available over here on mbed.