The STM32L031 with an SSD1306 OLED display

oled1

AliExpress supply a really tiny OLED display driven by an SSD1306 controller.
https://www.aliexpress.com/item/1pcs-0-96-white-0-96-inch-OLED-module-New-128X64-OLED-LCD-LED-Display-Module/32639731302.html

The actual display area is 25mm wide by 14mm high and has 128×64 pixels. Pixels can be white, yellow or black. The display connects to the host microcontroller over an I2C bus which greatly simplifies wiring.
stm32l031_ssd1306_circuit

Pixels are mapped to graphics memory as follows:
SSD1306GRAM

Each graphics memory byte controls a column of 8 pixels. 128 bytes cover a strip of 128×8 pixels or “a page” as its called in the data sheet. This display has 8 pages giving a resolution of 128×64 pixels. To set a particular pixel, you need to identify which page it is on and which byte and bit connects to it within this page. In theory you could then drive this pixel on or off as desired however there is a problem: graphics memory is written in byte-sized chunks so if you want to set a particular pixel, you need to read the byte connected to it, modify the bit in question and then write it back. Unfortunately, the I2C interface for the SSD1306 does not permit reading of graphics memory.
The Adafruit driver for this display gets around this problem by maintaining a copy of the graphics memory in the host MCU. It then updates the whole display when things change. This consumes a fair amount of RAM in the host MCU (128×8 = 1024 bytes) and is slow as the whole display must be updated if a single pixel changes.
My first attempt at controlling this display takes a different approach. The display is treated as a text only device capable of displaying 25×8 characters. Screen updates are carried out at page level i.e. 128 byte chunks are written at a time which corresponds to a single line of text. An I2C driver buffers this data and outputs to the display on an interrupt driven basis. This greatly reduces RAM consumption and speeds updates.

Programming and debugging


Openocd version 0.10 or later is required for the STM32L031. You may get this ready built for your OS or download the source and compile yourself. In my case, I downloaded the source from openocd.org. This was extracted and compiled as follows (as the root user):

apt-get install libusb-1.0-0-dev
./configure –enable-stlink –enable-maintainer-mode
make
make install

The STM32L031 was connected to the host PC using an ST-Link V2 clone and the debug/programming session was started this:

/usr/local/bin/openocd
-f /usr/local/share/openocd/scripts/interface/stlink-v2.cfg
-f /usr/local/share/openocd/scripts/target/stm32l0.cfg

GDB was then used to upload and test the program.

Source code

Source code is evolving and can be found here:
https://github.com/fduignan/stm32l031_examples

Detecting solar storms

I previously used a GY-652 module as the basis for a weather station. The module contains a BMP180 pressure/air sensor and a HMC5983 electronic compass. The weather station ran for about 4 months on 3 AAA batteries which was reasonable enough although the GY-652 had an unexpectedly large quiescent current of around 1mA.
Anyway, the batteries needed recharging and I decided it would be interesting to see if the HMC5983 could be useful in such an environmental monitoring test.
The Earth’s magnetic field varies from around 250 mG to about 650 mG. During solar storms, this can vary by as much as 170 milli-Gauss (source: http://www.spaceweather.gc.ca/svr-en.php). Daily variations in the magnetic field are of the order of 0.4 milli-Gauss. Are these measurable by the HMC5983 I wonder? According to its datasheet, the noise floor for the device is 2 mG so that probably rules out the detection of daily field variation. Storms are another matter however and these should be measurable by the device.
Batteries were recharged and code modified to make use of the HMC5983. The base-station for the weather station is now connected to a Raspberry Pi which relays the data up to Thingspeak. I’m going to leave it for a month or as long as the batteries hold out and, who knows, maybe it will spot a solar storm.
Click here to view the data on Thingspeak.
Update 20 January 2018
After a month or so in operation I think I *may* have seen some correlation between solar activity and magnetic reading variations. There is also some correlation between the magnetic field readings and temperature. The system had been using 3 rechargeable AAA batteries which have been drained twice (need to work on my low power mode :).
To overcome some of these shortcomings I’ve opted to move the sensor out of the attic and placed it under the stairs (unheated area, not too near any big electrical or magnetic fields). I’ve also replaced the AAA batteries with AA ones. Now just have to wait for a big solar storm…

Problem with mbed and the STM32L432KC

I’ve been trying to program the STM32L432 Nucleo-32 board using the mbed compiler. It has not been working. I can program a range of other boards without any problem. I noticed that the programming LED did not change when I tried to program the device and remembered that I had seen this before. The problem seems to be as follows:
When you load a program on to an MBED enabled Nucleo-32 board the firmware in the loader checks to see if the first 32 bit word (which will form the initial stack pointer) lies within the available RAM on the target. The value output by the mbed compiler for the L432KC is 0x20010000 – the top of the 64kB of RAM. This should be ok (you would think) however it is rejected by the loader. Using a hex editor I changed the first word to 0x2000C000 (the top of a 48 kB block of SRAM1) and it worked.
So, either there is a bug with the mbed linker script and/or initialization files or the loader on the Nucleo board is misinterpreting the available RAM.
Anyway, I’ve posted this as a bug(/fix) to mbed and will see what happens.

Update: There is updated firmware for the STM32L432 Nucleo board over here:
http://www.st.com/en/evaluation-tools/nucleo-l432kc.html It fixes this problem and the board accepts an initial stack pointer that lies within SRAM2 now.

SX1278 Module breakout board

sx1278breakout
I have been using some SX1278 modules for LoRa experiments. The pin spacing on these modules is 1.27mm – half of the pin spacing found on a breadboard. Interestingly, ribbon cable is exactly the right pitch and I previously managed to solder a length of ribbon cable to a module however, the wiring was prone to fatigue and soon became unreliable. Rummaging around my parts drawer I came across a 24 pin SOP24 to DIP adapter board. This is the sort of board you use with surface mount SSOP chips when you need to work with them on a breadboard. The spacing between pins on the SSOP pins is exactly 1.27mm. These adapter boards are very cheap ( < 10c) and it they can be used with the SX1278 as shown above to make it breadboard compatible

ESP32 and LoRa

ESP32_SX1278Module

Hardware

The ultimate intention of this project is to set up a LoRa to TCP/IP gateway. I decided that the ESP32 would make a good bridging device and found one here for about €6. The LoRa transceivers were obtained from Aliexpress also and are available here. Interfacing with the ESP32 was pretty straightforward as the LoRa module simply connects to its SPI interface. The really tricky part was connecting up to the 1/20 inch pitch connector on the LoRa module.

Software

I decided to develop my own LoRa driver module for the ESP32. Its API is based on the equivalent library for Arduino (I felt it would be easier to deal with just one API). Code was developed using the ESP development environment and is available here

Sender

Every conversation needs at least two parties so a device was needed to send some test data to the ESP32 board. A Seeduino was connected to an SX1278 LoRa module as shown here:
seeduino_lora
The great thing about the Seeduino is that you can switch its I/O to 3.3V which allows you directly connect it to the SX1278 module. The code (which makes use of the Arduino SX1278 library) is as follows:

#include <SPI.h>
#include <LoRa.h>

int counter = 0;

void setup() {
  Serial.begin(9600);
  delay(1000);
  //while (!Serial);

  Serial.println("LoRa Sender");
  // override the default CS, reset, and IRQ pins (optional)
  LoRa.setPins(7, 5, 6); // set CS, reset, IRQ pin
  
  

  if (!LoRa.begin(433123000)) {
    Serial.println("Starting LoRa failed!");
    while (1);
  }
  // The following settings should maximize reliability
  LoRa.setTxPower(20); // going beyond 10 is illegal
  LoRa.setSpreadingFactor(12);
  LoRa.setSignalBandwidth(125000);
  LoRa.setCodingRate4(5);
  pinMode(2,OUTPUT);
  Serial.println("Setup done");

}

void loop() {
  Serial.print("Sending packet: ");
  Serial.println(counter);
  digitalWrite(2,HIGH);
  // send packet
  LoRa.beginPacket();
  LoRa.print("hello ");
  LoRa.print(counter);
  LoRa.endPacket();
  digitalWrite(2,LOW);
  counter++;

  delay(4000);
}

Testing

The main performance measure I was concerned with was range as the data sizes were very small. The Arduino end of the radio link used the coiled quarter wavelength antenna supplied with the module. The ESP32 end used a straight quarter wavelength monopole. Range testing consisted of me walking around the neighborhood with the Arduino end in a bag while talking with my sons over the phone while they watched the ESP32 end. The ESP32 “base station” was located inside a house with foil backed insulation (which didn’t help). The testing was carried out in a suburban area with lots of houses, trees, cars and so on.

Results and discussion

The best range I managed to get was 770m (measured on Google maps). I suspect that this can be improved if I switch to dipole antennas and if I locate outside the foil envelope of the house. Watch this space 🙂