USB – Serial Debugger

This shows the moisture sensor from my previous entry with one exception – I added the USB Serial port. I have used various serial ports for debugging in the past, but never the USB despite I have always had it on my boards (almost always) – I simply never took the time to figure out how it worked before. And once I did I was puzzled about how easy it was.

Yes we have JTAG/SWD with single step debugger etc, but you do need a serial line to print info on during a real-time loop – not a lot of info, but triggered info about what is happening. You need this to verify that your system is working and to debug problems. Or to put it in a different way – you CAN manage without as I just had to in a project, but that will cost you a lot of extra hours.

USB is perfect for maintenance and debugging – it only takes two pins and all software topside and embedded is free – only a few lines of code and you have a debug serial running.

In this case I will also use LoRa and I will even look into having a LoRa based Bootloader. A bootloader is a small program that download and start the real program. To do so you need to do a few tricks that I will get back to, but it is not difficult.

 

Something else that is handy is a proper serial debugger/logger application. This is something I want to create and it is very straight forward. You make a debugger that send info in a selected format and then you add logic to the top-side utility to filter and process that. I tend to do this as per need, so I will look into creating a proper one that I can re-use more because it is an important part of your development time/productivity.

Home Automation – Moisture Sensor

It’s spring and summer comping up, and I am spending a bit of money on flowers every year so I would like to know more accurately their condition. Weather can change from very hot to very wet and not all plants get what they need from rain – some plants also get to much water etc. The solution to this is a moisture sensor that report soil moisture once an hour as illustrated below:

Using my classic block diagrams I use a Blackpill – cheap STM32F401CC breakout in dip40 format – as base and create the circuit abobe. I add a battery and remove things like leds and add a single sensor – a low cost moisture sensor consisting of two pins I stick into the soil. This gives resistance that decreases with moisture, so a very wet soil (water) will give value 0 (Zero). I removed the circuit that came with the sensor and powered it from a GPIO pin + connected a 10K resistor in serie – that gives a nice ADC reading after a few ms allowing me to switch the sensor on/off.

Temperature of electronics are not that accurate, but it is sufficient in this case and vref are internal. I also have VBAT (not shown) – I need to experiment a bit with VBAT and VREF, but I expect they will show me the condition of my battery – if not I will need to add another sensor measuring battery so I can predict battery condition/life.

The more complex circuit is the LoRa transmitter. I decided on a 5.- USD E220-900T22 D or S for the moment. These operate through UART and is very easy to use. I will need to put this in deep sleep and wake it up every time I want to send data. I also put a 32KB FRAM chip that uses the same footprint as SPI Flash on the back of the BlackPill – 32Kb is a lot of storage and FRAM is very low power and fast and can be written to almost forever. It is a good solution if you want to sample more often than you want to activate the transmitter. I am just experimenting with solutions here.

The illustration above is the actual finish sensor. I will use an old plastic pill-container, put the sensor through the lid and tighten it up consealing the electronics and transmitter inside – painten in black or dark green it will be “invinsible” in my garden between my flowes. I obviously need a matching adapter as gateway, so I decided on a Raspberry PI connected to mainst that will act as a gateway between LoRa and Wifi. I can connect UART + 3 GPIO pins from Raspberry PI directly to the LoRa unit.

It will be no PCB on first prototype, but I will order a PCB later – this is the first time I do a low power LoRa project so it is an experiment. STM32F401CC is not optional, but I still believe I should manage to get ca 1 year’s operation out of a 1Ah/3.7V battery – lets see.

The way this shold work is that the sensor sleep most of the time and measure moisture every 5th or 10th minute – reporting results every hour or so. Sending uses 110mA so I have to be carefully with not doing that to often or to long. One option to LoRa is ESP32 & Wifi, but I decided against that because of distances in my garden and the 3-4 seconds needed to connect Wifi + nice to actually do something withy LoRa as well.

Home Automation – Moisture ratnest

This picture shows my current development ratnest of a moisture sensor connected to a LoRa unit. It’s a few extra components that have nothing to do with this project, but it’s a good reminder why I seldom do breadboard ratnests anymore. The wiring failure trate is very high, but in this case it worked out well as I needed to test the moisture sensor and the LoRa unit (bottom left). I will put out a PCB for this soon. I decided to use a Blackpill that is a dip 40 breakout with a STM32F401CC on it. It has space for a SPI FLASH/FRAM on the back and have RTC oscillator etc. For it’s low price it is good value and an easy way around the current component shortage.

STM32F401CC is not low power, but my main concern here is the other components.

BSA – Abstraction Layer

This block diagram only illustrate a few of the modules in the Abstraction Layer – loads of more will be added as needed. I started this library years ago so it has existed in various versions, but are re-designing it to be cleaner now. This is a C++ library designed to make coding of user applications fast and easy and will contain a variety of modules that is needed organized in a matter that makes sence.

At the bottom you have native HAL drivers. ESP32, ESP8285, Pico and STM32 have different HAL libraries, and while it is funto write bare-bone libraries using registers etc it is also to time-consuming, so I leave that to people with more time & interest. I will write bare-bone and even assembly as needed, but only if needed.

Ethernet & Wifi are full TCP/IP stacks with extras.

LoRa is Long Range Radio covering up to 10km of range with reduced bandwidth.

Display is a generic display interface consisting of multiple LCD/TFT drivers.

FRAM and Flash are various persistent storage technologies.

GPIO, RTC, ADC etc are interface to various IO technologies.

System is a the rest including RTOS and timers, stuff that I want available on all platforms.

The cost of doing this is that I bloat the Flash a bit – currently working on STM32F401CC (Blackpill) I use ca 60Kb Flash (30 with optimization), so it’s not that bad. But, using this library on less than 128Kb Flash will be tight because it will have a lot of content. This is a trade – functionality – time to marked – versus flash.

I use this manually for now as I create the library, but BSA will generate code towards this.

BSA – Abstraction Layer – Documentation

If you have ever written a driver or picked up someone elses code you will recognize the need for documentation and examples on how to use it. I am writing loads of drivers these days and stugle with Vendor datasheets as well as HAL’s – usually I end up hacking and trying to understanding how it works and finally implement my own driver in a C++ class. If I left it there it would only be yet another layer to understand – for code to be really reusable you need documentation and examples.

Reading datasheets should normally solve the need for doc, but I often find that information is left out or that it actually work differently than expected – these details are crucial to get down in code and doc. Maybe it’s me who have misunderstood something, but if I don’t write that down you will struggle to correct my mistakes or improve my code.  I am no different than other developers – I like to do my own things – things that are productive for me. I have mates that do the same, but their things are seldom productive for me unless they bother to complete and document their work.

One of the worst tasks you take on as a developer is to try to fix someone elses work that is half-done, not tested and with no doc. That is what I am doing at moment – it’s no fun 🙁

Abstraction Layer – alSystem

I typically code ESP32, ESP8285, a lot of STM32, Pico, Linux and Windows to mention a few, so I want to be able to move code between those platforms with a minimum of hazle. For this purpose I create a C++ API that I call “Abstraction Layer”. I did not want to use the word “HAL” because it is not always hardware that I abstract away from. Another motivation for this layer is C++. Nearly all code, drivers etc for embedded is in C, while I have a very strong preference for Embedded C++.

The first class (module) I need is “alSystem” that allow me to access platform specific content in a generic way – simple things like sending signals, starting/stopping threads, tasks and timers and measuring time. Both FreeRTOS, Windows and Linux offer these mechanisms, but how do I move code between a non-RTOS, RTOS and Linux/Windows? The answer is that I hide them behind a class called alSystem.

Example:

uint32_t now = alSystem::millis();

This is a simple function to fetch the time in ms since the computer started. It is often used to measure time difference in ms, so I create a 32 bit version and a 64 bit version of this. I also create micosecond versions, but the later are seldom of any usage because we usually don’t maintain time below milliseconds unless we have special needs due to the complexity/cost/performance. The challenge with a 32 bit ms timer is that it wraps in ca 50 days, so if you want to make it secure you use a 64 bit version since uint64_t is supported.

  • millis()
  • millis64()
  • micros()
  • micros64()
  • delay()
  • sleep()
  • CreateTimer()
  • StartTimer()
  • StopTimer()
  • CreateTask()
  • SignalTask()
  • CreateThread()

These are a few of the static functions I always replicate in alSystem that will be guaranteed on any platform I use. Not everything is created inside alSystem as I also have a C++ library with power classes tailored specially for a static, embedded system where new/delete is forbidden – no memory allocation once the system is started.

This C++ layer do come at a cost of more Flash usage, but most MCU’s these days come with a bit of Flash and what I need the least is Flash I don’t use. This means I cannot use this library for very small cost-effective MCU’s, but for those I would code bare-bone C anyway. This library target more intelligent platforms where functionality and AI is in focus.

And least, but not last – generating code from BSA will use this library as a base – that simple trick allows me to generate code for multiple embedded platforms.

HMI – Display Abstraction Layer

I am working on “alDisplay” that is my embedded abstraction class to LCD/TFT diplays. This is quite a challenging task because I will have to deal with how specialized and proprietary these displays are and on top of that performance. The drawing above illustrate four layers – in my case actual hardware (orange) and three classes – I use alSPI, ST7735S and alDisplay. The user code will only see alDisplay and a very normalized interface, so if I swap display hardware that will be invisible to my code.

alDisplay will typically have functions like clear screen, println etc.

ST7735S will have a normalized interface for the alDisplay class and a specialized, proprietary interface for SPI.

alSPI have a normalized SPI interface.

This will bloat a bit, but as I will be using various displays it is a must.

Performance is a bitch as it might force me to use proprietary operations all the way from alDisplay with “#ifdef” code, but usually performance on a display is achieved through some memory moves that I hope to make common.

As for the reason I started this ? Available drivers will force my code to handle display differences, but more important I tested a display and a driver that don’t work. I might have a broken display, but decided that this layer have to done if I want to avoid spending endless time on different displays. I am also a bit fuzzed by depanding simple, straight forward, easy to read code!

SPI Error

This is an error! in my wisdom I decided to always select a SPI Flash not realizing that CS signal mark the beginning and end of each command. nCS needs to be connected to a GPIO pin for SPI based memory to work.

Home Automation – Battery lifetime

Ferrite RAM is one of the older RAM techniques used on old mainframes dating back to the 60s, but have in recent years also been popular as a replacement for EEPROM and flash because it is fast and have close to unlimited rewrites. I recently purchased a bunch of FRAM chips with the same interface as SPI flash in SOP8 format. This is excellent for the Blackpill that is a STM32F401CC  breakout board with space for a SPI Flash on the back since they use the same footprint. These FRAM chips cost around 4.- USD and will only have 32Kb (kilo byte), but that is excellent for my usage.

STM32F401 is not ideal for a low power battery operated application, but with the current MCU shortage I decided to give it a try since it is available with a breakout costing around 3.- USD in dip40 format.

Despite being hight performance and not low power the STM32F401CC is still capable of dropping down to uA while using only RTC and executing sampling using < 10mA. Reading an ADC (temperature) can be done once a second at very low power on a 1000mAH, 3.7V battery. And with a FRAM we can wake it up, read and write to it and be done within 5ms giving the battery a decent lifetime.

My first application is a simple temperature sensor that uses radio. I will sample to a FRAM and then connect to a server and transfer the FRAM content. To do this I create a simple array on FRAM storing time and temperature every second and every 10 minute or so I wake up a LoRa and send the content starting over.

I considered using Wifi (ESP), but realized that connection time on any Wifi unit would be 3-7 seconds with 150ich mA – far to high for battery usage. So I will test LoRa. The first usage will be 1-2 units only for testing.

I have also purchased STM32L4P5CE MCU’s that is much better on low power than STM32F401CC, but these are also more expensive and difficult to get by these days. And it is not the current usage of the MCU that is my main concern – it is everything around the MCU – Temperature sensor, FRAM, LoRa sender etc.

I plan to add far more sensors than temperature, but I need to experiment with low power solutions and temperature is a simple, good start.

In the Blackpill case I will need to put FRAM in sleep (12uA) and wake it up which takes ca 0,5 ms – while operating it uses 2.3 mA and I think I can finish this part within 1-2 mS which will be great for my battery budget.

Home Automation – Wifi battery operation time

One of the challenges I face using wireless communication is that it requires a bit of power/time to send signals.

Wifi : ESP-M1 is the first solution I evaluate. This is the smallest of the ESP modules and can send using 120-170mA. Simply speaking if you have a battery with 1000mAH or( 1AH) you can operate non-stop in 5-7 hours. So the classic technique is to switch off and only send then you need to. But, even in Rx mode we use typically 20mA and using that as base we have 50 or so hours of operation. We are still far off where we need to be. Deep sleep mode for this ESP module is <10uA according to the datasheet. 1000 / 0.01 = 100,000 hours meaning we suddenly talk about 10+ years in that mode assuming we use a 1AH battery. But, I can’t do much in deep sleep mode and I am surprised over how long time I use to connect and send. Testing on my table suggest up to 10 seconds since the ESP-M1 needs to connect to Wifi first.

Assuming I send once per minute (using 10 seconds to send) that gives me a ruff calculation 1000 / 200 * 6 = ca 30 hours of operation. Every 5 minute is 150 hours of operation etc – every hour ca 1800 hours operation etc. These calculations are a bit ruff, but they show that with 10 seconds connect & send time we will be struggling unless we upgrade once per 24 hour in which case we would be up in ca 43200 hours. The key here is to see if we can reduce that connect and send time. I think that reporting once per minute is a minimum for sensors.

ESP-M1 have a deep sleep mode that uses 20mA that remain connected to Wifi, but that alone will reduce us to 1000/20 = ca 50 hours of operation. The reality is that we need an average current consumtion on ca 100uA or lower for a 1AH battery to last a year on that battery.

Testing ESP32 connection time I get connected within 5 seconds, while using ESP-M1 I need closer to 10 seconds, so a classic ESP32 might be more optional that ESP-M1 for low power. But, we are far away from any solution that gives us 1 year+ on a 1000mAH battery. I need to dig further into this as using Wifi + battery is my first choise.