Categories
arduino C/C++ circuits Coding Embedded esp32 esp8266 expressif Internet of Things microcontrollers

ESP-32 : How to write multi-threaded application with priority, CPU core affinity, asynchronous non-blocking event driven loop.

Espressif Systems Shanghai launched the game changing low cost ESP-8266 microcontroller in 2014 – a key enabler for embedded Internet of Things (IOT) development.

Internet of Things (IOT)

Adding WiFi 802.11 and Bluetooth LE wireless connectivity to system on a chip (SoC) product costing roughly price of a cup of coffee meant innovators and micro electronics DIY enthusiasts could easily interface edge smart sensor or legacy hardware systems with cloud or mobile devices.

The successor chip ESP-32 (launched 2016) introduced Xtensa LX6 processor and FreeRTOS (a real time operating system (OS) for embedded) – enabling multi-threaded applications running on multi core CPU architecture.

To add context, many even relatively complex tasks especially automation, can be run on tiny embedded 8 bit processor such as Arduino. More complex applications, video, audio signal processing, image recognition or AI require much more compute power.

ESP-32 in the eco-system sits between Arduino and more powerful systems Raspberry Pi, Windows or Embedded Linux.

Sounds great, but how does it work in practice?

ESP8266

In this sketch ( https://github.com/steveio/arduino/tree/master/ESP32GPSMultiTask ) we assemble a UBLOX GPS data logger with an SD card internal storage, LoRaWAN wireless relay and an OLED display.


The goal is to demonstrate running 4 separate non-blocking tasks concurrently using FreeRTOS to schedule tasks, suspend, interrupt, queue and share data in thread-safe way with mutex semaphore locks.

Let’s take a look at the code…. concentrating on FreeRTOS multi-core multi-thread potential, rather than peripherals / sensors.

Data Structures, Multi-thread Semantics & Setup()

First, a struct to encapsulate core data model message (GPS data):

// GPS position data
struct XPosit
{
float Lat;
float Lon;
float Alt;
float Course;
float Speed;
} xPosit;

Next two semaphores to serialise reading and writing tasks to ensure data consistency:

// Semaphores to lock / serialise data structure IO
SemaphoreHandle_t sema_GPS_Gate;
SemaphoreHandle_t sema_Posit;

Then a pointer queue for passing messages between threads / tasks:

// GPS position data queue
QueueHandle_t xQ_Posit;

Here is the related setup():

sema_GPS_Gate = xSemaphoreCreateMutex();
sema_Posit = xSemaphoreCreateMutex();

xSemaphoreGive( sema_GPS_Gate );
xSemaphoreGive( sema_Posit );

Now let’s define 4 tasks:

// task handles
static TaskHandle_t xGPSTask;
static TaskHandle_t xLoRATask;
static TaskHandle_t xSDWriteTask;
static TaskHandle_t xOLEDTask;

// hexadecimal notification code
define GPS_READ_BIT 0x01
define LORA_TX_BIT 0x02
define LORA_RX_BIT 0x04
define SD_WRITE_BIT 0x06
define OLED_BIT 0x08

ISR / Interrupt for Asynchronous Non-Blocking Event Loop

The system will be driven by a periodic ISR timer raising an interrupt calling a handler routine running & managing tasks.

Keeping loop() empty results in non-blocking program execution, no waiting on calls to delay() in main routine – and we only need to call ISR once every ten seconds to read GPS (rather than polling loop()) – which is better for low power consumption.

Under the hood FreeRTOS works in the same way:

> FreeRTOS implements multiple threads by having the host program call a thread tick method at regular short intervals. The thread tick method switches tasks depending on priority and a round-robin scheduling scheme.
( https://en.wikipedia.org/wiki/FreeRTOS#References )

With a task based modular event driven architecture, failure of one task – if write to SD card task blocks or fails because there is no storage card present), other tasks – reading GPS messages, LoRaWAN radio transmit continue.

// ISR timer
hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
unsigned long isrCounter = 0;

ISR Handler Function (Main control routine, replaces loop()):

// ISR Interupt Handler
void IRAM_ATTR fLoRASendISR( void )
{
portENTER_CRITICAL_ISR(&timerMux);

// Main program routine here ..

portEXIT_CRITICAL_ISR(&timerMux);
}

In setup() we schedule ISR timer:

// Configure Prescaler to 80, as our timer runs @ 80Mhz
// Giving an output of 80,000,000 / 80 = 1,000,000 ticks / second
timer = timerBegin(0, 80, true);
timerAttachInterrupt(timer, &fLoRASendISR, true);

// Fire Interrupt every 10s (10 * 1 million ticks)
timerAlarmWrite(timer, 10000000, true);
timerAlarmEnable(timer);

FreeRTOS Task (Thread) Setup

Next in setup() we describe & initialise tasks, specify which CPU core they should run on, their priority and a few other details

// create a pointer queue to pass position data
xQ_Posit = xQueueCreate( 15, sizeof( &xPosit ) );

Serial.print("Start Task fGPS_Parse() priority 0 on core 0");
xTaskCreatePinnedToCore( fGPS_Parse, "fGPS_Parse", 1000, NULL, 0, &xGPSTask, taskCore0 );
configASSERT( xGPSTask );


Serial.println("Start Task fSD_Write() priority 4 on core 1");
xTaskCreatePinnedToCore( fSD_Write, "fSD_Write", 1000, NULL, 4, &xSDWriteTask, taskCore1 ); // assigned to core 1
configASSERT( xSDWriteTask );

Serial.println("Start Task fLoRA_Send() priority 3 on core 1");
xTaskCreatePinnedToCore( fLoRA_Send, "fLoRA_Send", 1000, NULL, 3, &xLoRATask, taskCore1 ); // assigned to core 1
configASSERT( xLoRATask );

Here’s how to have a task start and wait suspended pending a notification. LoRaWAN transmit task starts and waits until read GPS notifies of new message to send; no need to constantly run radio draining battery or poll the message queue.

for( ;; )
{
/* block until task notification */
xResult = xTaskNotifyWait( LORA_TX_BIT,
                     ULONG_MAX,        /* Clear all bits on exit. */
                     &ulNotifiedValue, /* Stores the notified value. */
                     portMAX_DELAY );  /* Block indefinately */

if( xResult == pdPASS ) 
{

Executing FreeRTOS threads : ISR Main Routine

In main ISR program loop, here’s how to notify tasks:

BaseType_t xHigherPriorityTaskWoken = pdFALSE;

/* Notify (trigger) Read GPS
xTaskNotifyFromISR( xGPSTask,
                   GPS_READ_BIT,
                   eSetBits,
                   &xHigherPriorityTaskWoken );

/* Notify LoRA send task to transmit by setting the TX_BIT */
xTaskNotifyFromISR( xLoRATask,
                   LORA_WRITE_BIT,
                   eSetBits,
                   &xHigherPriorityTaskWoken );

Inter process Communication (IPC): Semaphore Mutex Locks

Concurrency & data consistency in real time and multi threaded systems requires care to ensure serial ops – a write by one thread to a data struct for example must not corrupted by another concurrent thread.

The classic answer to this is locks – mutex, semaphores. A thread requests obtains & takes a lock and other tasks block (wait, retry). The lock is revoked only when lock holding task completes or rolls back.

Here is how it’s done in FreeRTOS:

   if ( xSemaphoreTake( sema_GPS_Gate, xTicksToWait0 ) == pdTRUE )
{
 if ( xSemaphoreTake( sema_Posit, xTicksToWait1000 ) == pdTRUE )
    {
      xPosit.Lat = gps.location.lat();
      xPosit.Lon = gps.location.lng();
      xPosit.Alt = gps.altitude.meters();
      xPosit.Course = gps.course.deg();
      xPosit.Speed = gps.speed.kmph();

      xSemaphoreGive( sema_Posit );
    }


    if ( xSemaphoreTake( sema_Posit, xTicksToWait1000 ) == pdTRUE )
    {
      Serial.println("xQueueSend()");
      pxPosit = &xPosit;
      xQueueSend( xQ_Posit, ( void * ) &pxPosit, ( TickType_t ) 0 );  
      xSemaphoreGive( sema_Posit );
    }

    xSemaphoreGive( sema_GPS_Gate );
  }

FreeRTOS : Message Queue

Those interested in using Queue’s (a sized FIFO pointer linked buffer) to pass data between tasks should consult the API reference:

https://www.freertos.org/a00018.html

// Examples:

// Writing
if ( xSemaphoreTake( sema_Posit, xTicksToWait1000 ) == pdTRUE )
        {
          pxPosit = &xPosit;
          xQueueSend( xQ_Posit, ( void * ) &pxPosit, ( TickType_t ) 0 );  
          xSemaphoreGive( sema_Posit );
        }

// Reading..
struct XPosit xPosit, *pxPosit;
  
 if( xQueueReceive( xQ_Posit,
                         &( pxPosit ),
                         ( TickType_t ) 10 ) == pdPASS )
      {

Conclusion

Espressif changed the game in 2014 with ESP8266 – bringing WiFi & Bluetooth to Arduino’s eco-system of low cost interoperable sensors & components – the internet of things (IOT) long promised since at least 1980s as “smart home” concept finally came of age.

Modern mobile devices and laptops are now so incredibly complex as to be virtually indecipherable, especially at hardware / OS level – to most people, even those within IT industry.

Arduino made it possible for students, DIY enthusiasts, makers & researchers to work with Microcontrollers in a way that is relatively simple, comprehensible and fun. No longer do you necessarily need a PhD, work for tech giant or own a CPU manufacturer to build a micro-electronics project.

By adding WiFi, Bluetooth & LoRaWAN wireless, Espressif opened door to new information age of cloud connected smart sensor & control devices. IOT smart home devices & solutions from Amazon AWS and Google (Matter) are already available in marketplace.

But the option endures – those who wish to can make their own or projects or use a component approach to Internet connect existing machines.

It remains to be seen in decades to come whether people use this tech for good, to benefit people, to make things better; or for nefarious activities – stalking, tracking, surveillance; or worse AI enabled weapons…

(my plan for this prototype was to add 6 axis accelerometer, attach prototype to a stunt kite, aquire some flight input data, create some cool real time web visualisations and explore some more Python SciPi, Mathplotlib & Pandas libraries (alas the prototype proved too fragile to get off the ground).

Anyway hope this tutorial helps you, it took me 30 years to even vaguely understand micro electronics hardware, multi-threading, c++…

Categories
arduino C/C++ circuits Coding Embedded esp32 expressif Internet of Things microcontrollers MQTT sensors weather station WebSockets

SparkFun Weather Sensor Kit

Wind and Rain sensor kit newly arrived from SparkFun Electronics to upgrade an Arduino Weather Station project.

SparkFun Weather Sensor Kit, DIY prototypes, Arduino Weather Station

Also pictured are earlier DIY prototypes – a childrens bee wind spinner with hall effect sensor to count rotations, an anemometer made from recycled plastic packaging utilising a IR Led optical rotary encoder and a wind vane with eight fixed directional magnetic switches.

( more here: http://www.steveio.com/2020/07/21/weather-station-wind-vane-history-science/ and http://www.steveio.com/2020/07/21/weather-vane-hall-sensor-magnetic-rotary-encoder/ ).

Bee Windmill Anemometer with ESP32 LoRa Transmitter running on single 3.3v Li-Ion cell.
8 Durection WInd Vane with magnetic hall sensor array and WebSocket TCP web browser interface.
ESP8266 Anemometer with optical IR Led sensor, wifi connectivity and D3.js websocket provisioned UI.

( Code for these projects can be found on GitHib. )

Weather station projects are a popular accessible introduction to microelectronics; a microcontroller and sensors can be found at low cost, modular hardware design results in easy assembly and open software platforms like Arduino IDE streamline packaging and deployment of code to devices.

Analysing real time or historical time series data, from weather sensors is a lot of fun. Frameworks like R Project for Math Stats: https://www.r-project.org/ ) and Python, Pandas, Numpy & Mathplotlib provide implementations of most alogirithms and convenient data structures for importing & manipulating data.

Techniques and methods are transferable and can be applied to other domains or ontologies – finanicial, accounting data for example.

SparkFun offer an OEM Wind & Rain sensor kit manufactured by Shenzen Fine Offset Electronics, China.

With advent of 3d modelling & printing it is also feasible for an enthusiast to design and fabricate via a 3d printer custom sensor components, perhaps using template models downloaded from repos like ThingiVerse.

In competition marine OpenWind are defining what smart network connected sensors can achieve utilising Bluetooth LE to make near real time wind data available on smartphone.

Assembled SparkFun Weather Sensor Kit

Ideal for enthusiast or educator SparkFun Weather kit comes wihout circuitry,  microcontroller or software.  An add-on PCB designed for use with  Arduino / ESP32 can be purchased or Datasheet Technical Specs provide reference sensor circuit designs, not significantly complex due to use of magnetic reed switch and variable resistance technology.

MCU Sensor Control & Relay Unit – IP67 Weather Proof Enclosure, ESP32 TTGO LoRa microcontroller, light, temperature and air pressure sensors.

Traditionally 433MHz RF has been used for base station to transmitter devices. A popular project is to use Arduino, a cheap 433Mhz receiver and a library to read data from a commercial weather station designed for use with manufacturers display, enabling this data to be provisioned to the cloud.

For data transmission non GPRS (cellular) options include Bluetooth LE (range ~100 metres) or LoRa (Long Range Low Power Network – range between 300 – 10km depending on antenae) offering cableless wireless connectivity allowing remote sensor situation with no associated network costs.

At data layer WebSockets and MQTT for IOT devices are challenging serial protocols as defacto lightweight, reliable & easy to implement transport relays.

Apart from range and connectivity goals of low power consumption for efficient and long battery running time combined with solar charging enable devices to run standalone for long periods.

Is a single 3.3v Li-Ion Battery Cell Sufficient? TP405 Charging Module & Solar Panel

Weather Stations have applications beyond meteorology in smart agriculture, industrial, safety monitoring and for wind or wave based leisure pursuits. 

Assembling DIY Arduino Mega Weather Station v1.0

More generally Internet of things wireless networked smart sensor platforms can be used for many purposes and combined with AI and Machine Learning algorithms useful insight and patterns within data can be analysed, classified and predicted. 

SparkFun Smart ETextiles & Conductive Thread Kit

Personally, I really enjoyed SparkFun Arduino LilyPad e-textile, smart fabrics and conductive thread kit, so looking forward to now spinning up the Weather Station sensors!

Categories
arduino C/C++ Coding Embedded esp32 esp8266 Internet of Things microcontrollers Software

Timed Device – Simple & lightweight scheduling for on/off devices

Timed Device is a library for Arduino / ESP 8266/32 embedded platforms written to emulate a simple plug in timer for an electrical device, where pins in a ring are set to define hour/minute status. Designed to be simple and lightweight optimisation is for low memory.

Arduino ATMega328 and related embedded microcontrollers provide internal high precision clock based timers suitable for events with second, millisecond and fine granularity (to clock speed 8 / 16 mhz).

When lower precision is sufficient, recurring timers for example where an event should occur on a specific minute, hour or day of week, a lightweight implementation can be achieved and storage for internal data structures can be optimised, a significant win factor on embedded systems where memory use is constrained.

The library follows an object orientated design pattern resulting in an extensible, scale-able architecture suitable for controlling from one to a large number of devices sharing a common timing core with each timed device class able to implement specific instructions for switching on/off.

Example use cases:

  • Switch lights or any electrical relay device on/off
  • Activate a pump or valve
  • Open/Close blinds, curtains or shutters
  • Control a fan, heat source or air conditioning

How to Define Time

In C time.h the tm structure has the following definition −

struct tm {
   int tm_sec;         /* seconds,  range 0 to 59          */
   int tm_min;         /* minutes, range 0 to 59           */
   int tm_hour;        /* hours, range 0 to 23             */
   int tm_mday;        /* day of the month, range 1 to 31  */
   int tm_mon;         /* month, range 0 to 11             */
   int tm_year;        /* The number of years since 1900   */
   int tm_wday;        /* day of the week, range 0 to 6    */
   int tm_yday;        /* day in the year, range 0 to 365  */
   int tm_isdst;       /* daylight saving time             */
};

While this structure is useful for point in time defined events, in case of a recurring timers this can be simplified.

If hour precision is sufficient, a single 32 bit mask can be used:

// Bitmask defines hours (from 24h hours) device is on (1) or off (0)
// 0b 00000000 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
long hourTimerBitmask =0b00000000001111111111111111100000;

Day of week (bit index 0 = Sunday – 6 Saturday) can be defined similarly:

long dayOfWeekTimerBitmask = 0b0101010;

With bitmasks we can determine if an event is scheduled using bit shifts:

// Check if bit at pos n is set in 32bit long l
bool Timer::_checkBitSet(int n, long * l)
{
  bool b = (*l >> (long) n) & 1U;
  return b;
}

To check if an hour timer bitmask is on/off for 7am:

bool isSet = _checkBitSet(7, hourTimerBitmask)

For a more detailed guide to Bitmasks, shifts and bitwise operations see this article.

An on / off time occuring at specific (hour:minute) intervals can be defined as:

typedef struct tmElements_t {
  uint8_t Min;
  uint8_t Hour;
};

We can use a struct as container for an array of 1..n pairs of on/off times occurring on specified weekdays:

#define SZ_TIME_ELEMENT 2

typedef struct tmElementArray_t {
  unsigned long Wday;   // bitmap - days of week (bit index 0 = Sun - 6 Sat)
  struct tmElements_t onTime[SZ_TIME_ELEMENT];
  struct tmElements_t offTime[SZ_TIME_ELEMENT];
};

With these data structures we can setup up a pair of on/off times (08:00 -> 08:10 and 19:00 -> 19:30) to occur on Sun, Mon, Thur, Fri:

// create variables to define on/off time pairs
struct tmElements_t t1_on, t1_off, t2_on, t2_off, t3_on, t3_off;
struct tmElementArray_t timeArray;

t1_on.Hour = 8;
t1_on.Min = 0;
t1_off.Hour = 8;
t1_off.Min = 10;

t2_on.Hour = 19;
t2_on.Min = 0;
t2_off.Hour = 19;
t2_off.Min = 30;

timeArray.n = 2; // number of time pairs
timeArray.Wday = 0b00110011; // define days of week timer is active on

timeArray.onTime[0] = t1_on;
timeArray.offTime[0] = t1_off;

Check if a recurring timer is set

How we check a timer (whether bitmask or time based) depends on the type of time source we have.

Most familiar time source on Arduino is millis() function providing elapsed time in milliseconds since device reset / startup.

This is useful for timing events in a non-blocking way at recurring intervals:

if (millis() >= sampleTimer + sampleInterval)
{
  ... do something
  sampleTimer = millis();
}

But what if we want to schedule in terms of hour, minute or day of week based on current time?

To achieve this a micrcontroller is combined with a Real Time Clock module, providing a battery backed time source that once synced (for example to an NTP time source) maintains current time even when device is switched off.

Arduino Nano in pin breakout board with DS3231 Real Time Clock Module

Arduino DS1307/3231 RTC modules have supporting libraries (for example RTCLib from Adafruit) to obtain current time usually in form of:

DateTime now = rtc.now();

Where DateTime object provides an API to return specific time elements:

Serial.print(now.hour());
Serial.print(':');
Serial.println(now.minute());
Serial.print(now.dayOfTheWeek());

Is an event scheduled (at a specific point in time)?

In C function overloading can be used to provide multiple interfaces to an isScheduled() method according to timer precision / time source type :

bool isScheduled(int h);
bool isScheduled(int h, int d);
bool isScheduled(int m, int h, int d);
bool isScheduled(unsigned long ts);

(Where h = hour, m = minute, d = week day)

Most users of Unix based systems are familiar with “timestamp” a 32bit unsigned long representing elapsed seconds since a fixed point in time, the Unix Epoch which occurred 1970-01-01 00:00:00 UTC.

Unix is not unique in using a reference Epoch, they have been used in calender systems worldwide since ancient times.

We can obtain current timestamp on Linux via command line with date command:

stevee@ideapad-530S:~$ date +%s
1620155520

A timestamp as a time source can be used with any timer definition, whether point in time or recurring. It has advantage (compared to using numeric representations of individual time elements) that a single long number can be used for computation and conversion and we don’t need to worry about problems like variable number of days in month or leap years.

Timestamps and time and date conversion tricks

For a timer defining specific days of week, the first question might be how to obtain weekday from a unix timestamp (elapsed seconds since epoch)?

int weekday = (floor((ts / 86400)) + 4) % 7;

1970-01-01 was a Thursday, dividing timestamp by 86400 (number of seconds in a day: 24 * 60 * 60) gives number of days since epoch, adding 4 shifts start day to Sun and modulo 7 returns day of week.

To check if a timestamp is within range of one or more on/off time pairs (specified in hh:mm format as uint8_t Min; uint8_t Hour;) first we convert all time elements to elapsed seconds:

// convert fully qualified timestamp to elapsed secs from previous midnight
unsigned long elapsedTime = ts % SECS_PER_DAY;

unsigned long s1, s2, onTime, offTime;

// check each timeArray on/off pair
for (int i = 0; i < _timeArray->n; i++)
{
  s1 = _timeArray->onTime[i].Min * SECS_PER_MIN;
  s2 = _timeArray->onTime[i].Hour * SECS_PER_HOUR;
  onTime = s1 + s2;
  s1 = _timeArray->offTime[i].Min * SECS_PER_MIN;
  s2 = _timeArray->offTime[i].Hour * SECS_PER_HOUR;
  offTime = s1 + s2;

  if (elapsedTime >= onTime &amp;&amp; elapsedTime <= offTime)
  {
    return true;
  }
}

Conclusion

While TimedDevice library is non-blocking (it does not technique like delay()) it is not truely asynchronous or event driven as each device implementing a timer must poll for an event on each iteration of loop().

This could constrained to checking once per second/minute/hour but perhaps a better architecture would be to use either RTC DS3231 squarewave or Arduino internal timer to register and trigger an interrupt with an associated handler only when an event is due to occur.

Similarly if either a very large number of events are scheduled, or a large set of timed devices created, it would be sensible to register these with a scheduler (akin to a CPU scheduler) tasked with sorting, prioritising and actioning event queue in an efficient way, which might utilise a multi-threaded approach on multi-core CPU architectures.

Source code for Timed Device library can be found on GitHub including a full Arduino sketch example for defining a solenoid valve timer with an RTC timesource.

Categories
arduino Coding Embedded Internet of Things Python weather station

Sunrise / Sunset Time visualised with Python, Pandas & Matplotlib

We can use Python Pandas & Mathplotlib libraries to quickly visualise sunrise / sunset timing data, but how to plot time as a number on a graph?

Sunrise / Sunset times are computed on my Arduino Weather Station using SunMoon library ( https://github.com/sfrwmaker/sunMoon ). Incredible that such a tiny 8 bit machine architecture can run this relatively complex algorithm with ease.

Data is logged every 30 mins to daily files stored on SD card in JSON text format.

stevee@ideapad-530S:~/Arduino/projects$ ls  ./data/weather/*.TXT | head -3
./data/weather/20200816.TXT
./data/weather/20200817.TXT
./data/weather/20200818.TXT

Once files are transferred to a Linux computer, a bash script pre-processor (a useful technique on large datasets where syntax modifications are necessary) is used to reformat data as valid array of JSON objects –

stevee@ideapad-530S:~/Arduino/projects$ cat weather_preprocess.bash
#!/bin/bash

for f in data/weather/*.TXT; do
    j="${f%.*}"
    grep "^\[" $f | sed 's/\[//g' | sed 's/\]/,/g' | sed '$ s/.$//' | sed '$ s/.$//'  > $j.json
    sed -i '1 i\\[' $j.json
    echo "]" >> $j.json
    echo $j.json
done

JSON data is an array of objects where each row represents a single log entry indexed by unix timestamp.

Columns represent sensor & computed data – temperature, humidity, air pressure, sun elevation, surise / sunset time and moon phase.

stevee@ideapad-530S:~/Arduino/projects/python$ head -20 ../data/weather/20201015.json
[
{"ts":1602720026,"t":15.5,"h":88,"l":986,"p":102141,"t2":18.8,"a":0.414837,"w":697,"el":-47.86353,"az":2.618189,"lat":50.7192,"lon":-1.8808,"sr":"07:31","ss":"18:14","mn":28},
{"ts":1602720059,"t":15.5,"h":88,"l":988,"p":102140,"t2":18.8,"a":0.166463,"w":692,"el":-47.85925,"az":2.82748,"lat":50.7192,"lon":-1.8808,"sr":"07:31","ss":"18:14","mn":28},
...

Assuming a working Python (v2x) installation and dependencies (Pandas, Mathplorlib, Datetime) are present, we include required libraries and import data from file using Pandas creating a DataFrame in memory table structure –

import os
import json

import matplotlib as mpl
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import pandas as pd
from datetime import datetime as dt

days_to_extract = 90;
path = "../data/weather/"
files = []
frames = []

### Data file path and file format 
for (path, dirs, f) in os.walk(path):
    files = [ fi for fi in f if fi.endswith(".json") ]

### Load JSON data
def load_json_data(filepath, frames):
    with open(filepath) as f:
        d = json.load(f)
        df = pd.DataFrame(d)
        frames.append(df)

### process n days datafiles
for f in files:
    filename = path+f
    bits = os.path.splitext(f)
    datestr = bits[0]
    dtm = datetime.strptime(datestr, '%Y%m%d')
    if dtm >= datetime.now()-timedelta(days=days_to_extract):
    load_json_data(filename,frames)

# complete dataset as DataFrame
df = pd.concat(frames)

In dataset although frequency for sunrise / set times is daily, these are actually logged every 30 mins, creating many duplicate entries –

print df['sr'];

datetime
2021-01-19 00:00:26 17:37
2021-01-19 00:00:59 17:37
2021-01-19 00:01:26 17:37
2021-01-19 00:01:59 17:37
2021-01-19 00:02:26 17:37

To get one entry per day sunrise/set timing column data is resampled to daily frequency ( .resample(‘1D’) ) and any null rows are dropped with .dropna().

This is equivilent to a relational database roll-up or group by query.

sr = df['sr'].resample('1D').min().dropna()
ss = df['ss'].resample('1D').min().dropna()

Now we have a single daily time entry row indexed by date.

2021-01-18 17:35
2021-01-19 17:37
2021-01-20 17:38
2021-01-21 17:40
2021-01-22 17:41
Name: ss, Length: 93, dtype: object

To plot times on Y-Axis values from Pandas Series are extracted into a simple 2d array list.

We call datestr2num() from mathplotlib.dates ( converts date/time string to the proleptic Gregorian ordinal ) to format time as a number –

srt = np.array(sr.values.tolist())
srt = mpl.dates.datestr2num(srt)
sst = np.array(ss.values.tolist())
sst = mpl.dates.datestr2num(sst)

giving values that can be plotted –

[737824.30416667 737824.30486111 737824.30625 737824.30763889
737824.30833333 737824.30972222 737824.31111111 737824.31180556
…]

A linear scatter plot can then be rendered with a few formatting options specified

fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_title('Sunrise (GMT) Oct 2020-Feb 2021 for Bournemouth 50.7192 N, 1.8808 W')
ax.plot_date(sr.index, srt, '-ok', color='red', markersize=4)
ax.yaxis_date()
ax.yaxis.set_major_formatter(mdates.DateFormatter('%I:%M %p'))
fig.autofmt_xdate()

The result is a curve which shows day light hours being influenced as Mid Winter solstice (shortest day) Dec 21st is passed.

Sunrise times visualised as scatter plot
Sunset times

For more info and examples of real time series data charting in Python: https://www.dataquest.io/blog/tutorial-time-series-analysis-with-pandas/

Discussion of Arduino Sunrise / Sunset libraries: http://www.steveio.com/2020/09/03/sunrise-sunset-is-arduino-ahead-of-its-time/

Categories
C/C++ Coding Embedded microcontrollers Software

Binary – Bits, Bitwise Operators & Bitmasks

First we define bits, bit fields and bit masks, relating these to data structures, addressable memory storage, registers and binary protocol frames.

Bitwise logic is introduced with simple practical examples – bitwise operators and bit shifts – setting individual bits on or off, extracting bits for reading, manipulating bits or checking bit field values.

Bitwise Logic Gates

In Assembler, C and other programming languages bit manipulation is common especially when implementing low level hardware interfaces, embedded microcontroller based IOT devices, processing sensor, machine, network and peripheral data.

Graphics programming and image manipulation also utilises bitwise operations and bitmaps to perform animation (shifting and moving bits), compositing and pixel value modification.

Bits, Bytes & Bitmasks Explained

Bits are smallest unit of storage representing either a binary 1 or 0.

A byte is a group of binary digits or bits (typically eight) operated on as a unit.

Bit fields are a series or array of bits in adjacent memory.

Bit field values might represent a set of individual attributes (boolean on/off “flags” or status fields), a register value (storage address / instruction), or encapsulate binary encoded data.

AND bitmask

A bitmask (or mask) is an ordered set of bits of defined length used in bitwise logic to set, invert or manipulate another bit field.

Registers – Bit Arrays

Registers encapsulate a set of ordered bits in storage with a defined bit length typically expressed as power of base 2 (commonly 8 / 16 / 32 / 64 / 128 bit).

76543210
8 bit register where least significant bit (LSB) starts with (2º = 1)

An 8 bit register can hold unsigned (positive) numbers from 0 to 255 or signed (includes negative) values from -128 to +127 (where bit 7 is a sign bit).

Registers are used to address memory locations for computations within central processing unit, configuration parameters, IO from data storage, ports, or peripherals.

Bits in a Byte – Memory Address

Bitwise shift operators, along with logical AND can be used to access individual bits within a byte of memory for any variable in C.

To read value of bit at index 4 of an 8 bit length variable x

int x = 16; // 10000
printf("%d\n", (x >> 4) &amp; 0x01); // Prints 1

Network Protocols – Binary Bit Sequences

Bit sequences are used in network protocols where packetised messages are arranged into frames containing fields with defined offset and length.

A frame refers to the entire data packet which is being sent/received during a communication. Each protocol defines a specification of its own frame format.

An RS232 serial protocol frame defines a bit sequence –

Frame:StartDataParityStop
Size (bits):15-911-2
Bit sequence format of a USART serial protocol frame

Bitwise operators and shifts can be used to efficiently read, set or modify fields within a network protocol packet.

Bitwise Operators

Bitwise operations allow setting bit sequence values in a single operation and are more efficient than loops or maintaining individual bits.

SymbolOperator
&bitwise AND
|bitwise inclusive OR
^bitwise XOR (exclusive OR)
<<left shift
>>right shift
~bitwise NOT (one’s complement) (unary)
Bitwise and shift operators

Logical operators AND, OR, XOR (Exclusive OR) and NOT (Negation) implement bitwise manipulation, incurring a minimal number of processing instructions.

Comparing bit by bit –

  • OR sets a bit if one or both operators are true: 1110 OR 0000 = 1110
  • AND sets a bit if both operators are true: 1010 AND 1101 = 1000
  • AND with zero-check tests if any bit is set: 1010 AND 0010 = 0010 ≠ 0
  • XOR sets a bit if only one operator is true: 1010 XOR 0100 = 1110
  • NOT inverts all bits: NOT 1011 = 0100

Bitwise operators work on integer and character data types and do not modify value of their arguments so assignment (=, +=, -=, |=, &=) is typically used for example x |= (y & z).

Arithmetic Bit Shifts

Left Arithmetic Shift

Bit or Arithmetic shift operators << >> treat a value as a series of individual bits rather than a numerical quantity, shifting (or moving) bit positions left or right.

These operations are useful to move a bit or set of bits to a specific positional index.

In a left shift operation (<<) bits are moved by one position to the left and last digit is zero filled. This is equivalent to multiplication of a signed integer by a power of 2.

01101011
8 bit Binary encoding of decimal 107

Left shifting 2 bits ( << 2) results in

10101100
8 bit Binary encoding of decimal 172

A right shift operation (>>) moves bits right by a specified number of positions, dividing number by 2.

In more general form we can say –

// x multiplied by 2ⁿ
x << n
// x divided by 2ⁿ 
x >> N

Notes –

  • Bits shifted from end are lost due to overflow, they do not wrap around.
  • Right bitshift on signed types – gcc promises to always give the sane behavior (sign-bit-extension) but ISO C allows the implementation to zero-fill the upper bits.

Defining bit masks in C

Bitmasks are used in bit manipulation and combined with a logical operator (AND, OR XOR) define a pattern for bits to keep or discard.

In c++14 which supports binary literals bit masks are defined –

const uint8_t mask0 = 0b00000001 ; // represents bit 0
const uint8_t mask1 = 0b00000010 ; // represents bit 1
const uint8_t mask2 = 0b00000100 ; // represents bit 2
...  

Using Hexadecimal

const uint8_t mask0 = 0x01 ; // bit 0  0000 0001
const uint8_t mask1 = 0x02 ; // bit 1  0000 0010
const uint8_t mask2 = 0x03 ; // bit 2  0000 0100
...  

Or with bit shift operator

const uint8_t mask0 = 1 << 0 ; // 0000 0001
const uint8_t mask1 = 1 << 1 ; // 0000 0010
const uint8_t mask2 = 1 << 2 ; // 0000 0100
...

Set, Clear, Modify, Toggle & Check Bits

Set a bit
Set the nth bit ( zero up to n-1 ) of number

number |= 1UL << n

Set bit 0 of i to one

i |= 1 << 0;

// Example
000 i
001 1 << 0;
001 i |= 1 << 0;

  • the << operator is left “bit shift” operator which moves all bits to the left n times.
  • 1UL species numeric literal 1 with type Unsigned Long

Clear a bit
Set nth bit of number to zero

number &= ~(1UL << n);

Set bit 1 of i to zero

i &= ~(1UL << 1);

// Example
010 i
000 ~(1UL << 1)
000 i &= ~(1UL << 1);
  • ~ negates value of bit 1 from 1 to 0
  • AND evaulates false as both operands are not equal

Toggle (flip) a bit

Toggle nth bit of number

number ^= 1UL << n;

Toggle (flip) bit 0 of i

i ^= 1 << 0;

// Example
001 i
001 1 << 0
000 i ^= 1 << 0
  • ^= represents XOR exclusive OR with assignment, output is true when operand bits are different

Check a bit is set

True if nth bit of number equals 1

bit = (number >> n) & 1U;

Check bit 7 of i assign 1 or 0 to bit

int bit = (i >> 7) & 1U;

Modify – Changing a bit to x (1 or 0)

// 2s complement system with negation behaviour
number ^= (-x ^ number) & (1UL << n); 
// portable
number = (number & ~(1UL << n)) | (x << n);

Set bit 7 of i to x

i ^= (-x ^ i) & (1UL << 7);
i = (i & ~(1UL << 7)) | (x << 7);

Notes –

  • range / bounds checking is not applied, out of bounds shift index result in undefined behaviour
  • Use 1ULL if number is wider than unsigned long
  • endianess (position of most / least significant bit) and signing varies between platforms and compilers

Bitwise Functions as C Pre-Processor Macros

To minimise code duplication bit operator functions can be defined in a header file as pre-processor macros

/* a=number, b=bit index 0-n */
#define BIT_SET(a,b) ((a) |= (1UL<<(b)))
#define BIT_CLEAR(a,b) ((a) &amp;= ~(1UL<<(b)))
#define BIT_FLIP(a,b) ((a) ^= (1UL<<(b)))
#define BIT_CHECK(a,b) (!!((a) &amp; (1UL<<(b))))

Print Bits in C as Left Padded Binary String

In C printf() does not have a format specifier to print a string representation of binary, but we can write a function to achieve this –

const unsigned bits = 8;

// print integer value as a left padded binary string
void print_bits(unsigned value)
{
    unsigned mask = 1 << (bits-1);

    while (mask)
    {
        printf("%d", (mask &amp; value) != 0);
        mask >>= 1;
    }

    printf("\n");
}

int main()
{
  int i = 0x145;
  print_bits(i);
}

Result:
01000101

References and Further Reading:

[1] Methods for Bit Manipulation & Discussion:
https://stackoverflow.com/questions/47981/how-do-you-set-clear-and-toggle-a-single-bit/263738#263738

[2] Theory and examples of Bitwise Operation:
https://en.wikipedia.org/wiki/Bitwise_operation

[3] Bit Twiddling Hacks – Advanced Bitwise Algorithms http://graphics.stanford.edu/~seander/bithacks.html