Categories
Internet of Things Software Systems Administration

You’ve got mail! Goodbye SMTP?

Recently I have been busy upgrading our email server adding DKIM (DomainKeys Identified Mail), DMARC (Domain-based Message Authentication, Reporting, and Conformance) and SPF (Sender Policy Framework). The question – why has it taken so long for adoption of secure messaging standards?

Email or electronic mail emerged in 1970’s when academics began networking their computers and sought a standard for exchanging communications. By 1982 Simple Mail Transfer Protocol (SMTP) promised an interoperable standard (albeit one that is anything but “simple”) for connecting computers worldwide via an email service.

While the accomplishment of enabling a worldwide email network, given the plethora of machine types, operating systems and mail programs should not be overlooked, given that it has run now largely successfully for nearly 50 years, fundamental weaknesses in architecture were present from the beginning.

Principally, the original design of SMTP had no facility to authenticate senders, or check that servers were authorized to send on their behalf.

The absence of “trusted identity” is rooted in a more generalised flaw in design of public internet which grew around core of DNS – domain names (conconical textual names for Internet Protocol IP numeric addresses) representing computers on a network.

Inability to reliably verify sender/recipient has resulted in a worldwide public electronic messaging system which is unreliable, untrustworthy and fundamentally insecure.

On corporate LANs by mid 1990’s enterprise messaging typically provided by IBM’s Lotus Notes or Microsoft Exchange already had many features integral to a reliable and trusted messaging platform.

Identity could be resolved via LDAP to Active Directory – a secure enterprise wide inventory of devices and individuals or roles.

Messages could be cryptographically signed to maintain integrity and quality of service (QOS) ensured for example that a message was delivered, opened and acknowleged via a read receipt (did everyone remember to read the chairman’s annual review email?).

Strangely, on public internet, due in part I think to decentralisation, the absence of trusted federated identity protocol and relatively high computational cost of cryptography, features central to secure messaging were never adopted.

As a result even today, there is no certainty an email will be delivered. Or indeed that a message originated from the proported sender.

Which is perhaps acceptable in context of a casual social chat between friends. But when we are talking business correspondence, financial matters, government or legal communications – the result is an email infrastructure that is simply not fit for purpose.

Despite advances in textual heuristics based profiling/scoring, delivery whitelists / blacklists and categorisation of messages the prevalence of SPAM and message spoofing scams signpost the basic need to verify message origination and prevent impersonation has never been properly addressed.

DKIM which uses SSL identity certificates appended to DNS record and cryptographic key based message signing of fields in message envelope is a step towards establishing trusted identity.

We are not talking about message encryption here – arguably on public civilian internet maintaining a plain text message body as standard should endure.

Rather, trusted identity means we can be relatively (more) certain of who a message came from. Yes, that is a genuine message from your bank, utility provider, local government agency, legal department or tax office.

A related problem is in absence of a reliable directory service, how is it possible to know how to contact someone? Unlike enterprise LDAP public internet has never had a contact phone book. Trying to find an individuals contact details now – it is nearly impossible!

Having covered identity, lets consider other features missing from email.

Supported by postal services for many decades – guaranteed or priority delivery. Simply, the idea that a recipient must acknowledge receipt (typically with a signature).

Law enforcement presently tasked with delivering summons, court communications etc are spending a fortune in terms of Police time and endangering lives of rank and file when as recently as 1990’s in many cases a telephone call from station to a UK landline (registered to an address) was considered acceptable.

Contacting anyone reliably by mobile telephone and even more so via email – how is this possible today?

Also, the notion of message priority – I was at a music festival recently and a payment message from a food van online payment terminal could not get through as mobile network was saturated by live streamers sending social video content.

These use cases fall into a set of QOS (quality of service) features.

Looking ahead to internet of things when my house, car, toaster, smoke alarm, intruder detection system and billions of other smart sensor based devices are predicted to become internet enabled, trusted identify and QOS become absolutely vital.

Consider a smart home notifying fire department that a blaze is detected – reliability, message integrity and trust must be present in such a system from the start.

Having worked recently with message queue platforms (Hive/MQTT/Mosquito) on systems where message volumes and flow rates can be substantial, QOS that encodes TTL (time to live) and priority is vital in processing large message flows.

When a receiver is un-contactable or offline, messages may be stored for a period and discarded based on criteria like number of re-tries or after a given timeout. Try to deliver this message once this week, then discard it. SMTP does not have this class of feature.

At dawn of Internet of Things, where number of devices transmitting messages over IP networks is projected to grow exponentially, what features and requirements should a messaging infrastructure include? It seems trusted identify, security and QOS are central concerns.

The internet, at its core is fundamentally a messaging platform.

Email (SMTP) is outdated, lacking core features, unreliable and insecure. It should and probably will be superceded by message queue technology (MQTT publish/subscribe).

Ideally, any future protocol should continue to be an open standard backed by a global consortium such as RFC from Internet Engineering Taskforce (IEEE).

Goodbye Multi Mime Part Email. Goodbye SMTP. Goodbye SPAM. it was nice to know you!

Categories
C/C++ Coding Embedded Linux Software

Debug Linux Input Device Hardware

The trackpad on my Lenovo IdeaPad 530s running Linux started periodically freezing. How to debug the problem?

Lenovo 530s Laptop

An add-on PCB Assembly designed for use with Arduino / ESP32.

First, which version of Linux is running?

cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.1 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.1 LTS"
VERSION_ID="18.04"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

We are running Ubuntu Linux 18.04 (Bionic Beaver).

Now lets get Linux kernel and GCC versions.

cat /proc/version
Linux version 4.15.0-213-generic (buildd@lcy02-amd64-079) (gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)) #224-Ubuntu SMP Mon Jun 19 13:30:12 UTC 2023

Or, using uname command:

uname -a
Linux ideapad-530S 4.15.0-213-generic #224-Ubuntu SMP Mon Jun 19 13:30:12 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

So we can see 64bit (x86_64) AMD architecture with Linux kernel 4.15.0-213-generic which is standard version for Bionic.

Lenovo Ideapad 530s Trackpad input device.

What we know from viewing syslog ( /var/log/syslog ) after trackpad freeze a udev device “MSFT0001:01 06CB:7F27 Touchpad” is removed and module libinput is unloaded:

Feb 1 15:07:16 ideapad-530S /usr/lib/gdm3/gdm-x-session[3206]: (II) config/udev: removing device MSFT0001:01 06CB:7F27 Touchpad
Feb 1 15:07:16 ideapad-530S /usr/lib/gdm3/gdm-x-session[3206]: (**) Option "fd" "42"
Feb 1 15:07:16 ideapad-530S /usr/lib/gdm3/gdm-x-session[3206]: (II) event14 - MSFT0001:01 06CB:7F27 Touchpad: device removed
Feb 1 15:07:16 ideapad-530S /usr/lib/gdm3/gdm-x-session[3206]: (II) UnloadModule: "libinput"

Okay, now lets find the TouchPad input device.

We can list all loaded input devices and search for “Touchpad”:

sudo cat /proc/bus/input/devices

N: Name="MSFT0001:01 06CB:7F27 Touchpad"
P: Phys=i2c-MSFT0001:01
S: Sysfs=/devices/pci0000:00/0000:00:15.0/i2c_designware.0/i2c-3/i2c-MSFT0001:01/0018:06CB:7F27.0001/input/input16
U: Uniq=
H: Handlers=mouse0 event14
B: PROP=5
B: EV=1b
B: KEY=e520 10000 0 0 0 0
B: ABS=260800000000003
B: MSC=20

Or we can use contextual grep to extract just the entry for “touchpad”

grep -iA2 touchpad /proc/bus/input/devices
N: Name="MSFT0001:01 06CB:7F27 Touchpad"
P: Phys=i2c-MSFT0001:01
S: Sysfs=/devices/pci0000:00/0000:00:15.0/i2c_designware.0/i2c-3/i2c-MSFT0001:01/0018:06CB:7F27.0001/input/input16

This gives the device name “MSFT0001:01 06CB:7F27 Touchpad” and informs it is communicating over PCI bus using i2c.

I2C is a two-wire serial communication protocol using a serial data line (SDA) and a serial clock line (SCL). The protocol supports multiple target devices on a communication bus and can also support multiple controllers that send and receive commands and data.

In an age of ubiquitous USB peripherals i2c protocol is still widely used in laptops – on my Lenovo machine it interfaces power button, keyboard and trackpad.

Human Input Device (HID) over i2c is a protocol developed by Microsoft, the specification is available here and there is some interesting background in this document.

Okay, to see if we get any data from touchpad lets cat device file descriptor to see if any binary data is transmitted when touchpad is activated:

sudo cat /dev/input/event14
袛f�9h袛f�5�袛f�6�袛f�J袛f�E袛f��袛f��袛f�袛f�袛�6�袛f

To view input device data as text we can install and use evtest – Input device event monitor and query tool. The c source code for this helpful tool is available here.

sudo apt install evtest

Device details are displayed following a list of all supported events.

sudo evtest /dev/input/event14
Input driver version is 1.0.1
Input device ID: bus 0x18 vendor 0x6cb product 0x7f27 version 0x100
Input device name: "MSFT0001:01 06CB:7F27 Touchpad"
Supported events:
Event type 0 (EV_SYN)
Event type 1 (EV_KEY)
Event code 272 (BTN_LEFT)
Event code 325 (BTN_TOOL_FINGER)
Event code 328 (BTN_TOOL_QUINTTAP)
Event code 330 (BTN_TOUCH)
Event code 333 (BTN_TOOL_DOUBLETAP)

Event type 3 (EV_ABS)
Event code 0 (ABS_X)
Value 860
Min 0
Max 1224
Resolution 12
Event code 1 (ABS_Y)
Value 397
Min 0
Max 804
Resolution 12

Touchpad interactions are then reported:

Event: time 1721481862.564796, -------------- SYN_REPORT ------------
Event: time 1721481862.571954, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 437
Event: time 1721481862.571954, type 3 (EV_ABS), code 1 (ABS_Y), value 437
Event: time 1721481928.193073, type 1 (EV_KEY), code 330 (BTN_TOUCH), value 0
Event: time 1721481928.193073, type 1 (EV_KEY), code 333 (BTN_TOOL_DOUBLETAP), value 0

…………………………………………………..

To restart / reload the trackpad input device we have two tasks – Xorg windowing GUI input driver and Linux kernel hardware driver.

xinput is a utility to configure and test X input devices, such as mice, keyboards, and touchpads.

Lets get a list of all input devices registered with Xorg:

xinput --list
⎡ Virtual core pointer id=2 [master pointer (3)]
⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]
↳ MSFT0001:01 06CB:7F27 Touchpad id=10 [slave pointer (2)]⎣ Virtual core keyboard id=3 [master keyboard (2)]
↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)]
↳ Power Button id=6 [slave keyboard (3)]
↳ Video Bus id=7 [slave keyboard (3)]
↳ Power Button id=8 [slave keyboard (3)]
↳ Integrated Camera: Integrated C id=9 [slave keyboard (3)]
↳ Ideapad extra buttons id=11 [slave keyboard (3)]
↳ AT Translated Set 2 keyboard id=12 [slave keyboard (3)]

We can ask xinput for device (id=10) status:

xinput --list-props 10
Device 'MSFT0001:01 06CB:7F27 Touchpad':
Device Enabled (142): 1
Coordinate Transformation Matrix (144): 1.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 1.000000
libinput Tapping Enabled (279): 1
....
Device Node (267): "/dev/input/event7"
Device Product ID (268): 1739, 32551

Lets try restarting the x input driver using id or by device name:

xinput disable 10
xinput enable 10

xinput disable 'MSFT0001:01 06CB:7F27 Touchpad'
xinput enable 'MSFT0001:01 06CB:7F27 Touchpad'

Now lets find and restart Kernal touch input module by listing all loaded kernel modules, identifying touchpad driver:

lsmod | grep touch
hid_multitouch 24576 0
hid 122880 3 i2c_hid,hid_multitouch,hid_generic

This informs trackpad is loaded into kernel via module “hid_multitouch”. As this is open source we can take a look on git at the source code for this module.

To unload/reload the device as sudo root user:

sudo modprobe -r hid_multitouch
sudo modprobe hid_multitouch

Unfortunately, reloading module does not fix the frozen trackpad. Only a reboot gets it running again.

We could file a bug report with module developers but Ubuntu Bionic like our laptop is quite old now and so we will probably just update to latest Linux version on new hardware.

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 physical hardware / OS level – to most people, even those within IT industry.

Arduino makes it possible for students, DIY enthusiasts, makers & researchers to work with Microcontrollers in a way that is relatively simple, comprehensible and fun.

By adding WiFi, Bluetooth & LoRaWAN wireless, Espressif opened door to new innovatioon in information age of cloud connected edge sensor & control devices.

Categories
Uncategorized

OneWorld365 : Work, Volunteer, Travel Worldwide

How do you develop a website while backpacking round the world without carrying a computer?

How was travel directory OneWorld365 (www.oneworld365.org) created?

In 2007 graduating with degree in media from Liverpool University and having worked twice in USA at Summer Camp younger brother Paul approached elder brother Steven, a software engineer with idea to publish a website listing summer camp placements.

This expanded to include a range of Gap Year activities including volunteering, language schools, seasonal & work abroad.

At that time a few websites had regional listings but there was nothing searchable.

In library publisher Susan Griffith offered a good guidebook with listings and Lonely Planet also offered advice.

Our idea was to offer free listings to non-profits, NGO’s & grass roots organisations and options for priority listings to bigger travel providers. You could go direct to project provider or book all inclusive through a reseller.

Realizing that taking booking & organising placements required a large team, we decided instead to advertise the idea of meaningful travel possibilities.

Soon we found in addition to students many seniors & corporate workers on sabbatical were also interested in a meaningful travel experience with local people’s.

The website and CMS was developed during a round the world backpacking adventure using internet cafe computers and VI (putty).

Whilst most travellers in cybercafe were checking their Hotmail and some were Skyping home on video call we were typing furiously into a Unix terminal emulation shell prompt.

Having little money for resources and with a rapidly growing audience on technical side we started with a virtual hosting running BSD Unix from Texas data centre and used open source technologies..

Currently the site is hosted in Frankfurt with IONOS in VPS virtual server running CentOS Linux, PostgresSQL, PHP, NGINX and Apache SOLR Lucerne.

Essential maintenance is mostly now carried out using SSH from Android Mobile phone.

Do we need to develop an App? Wasn’t promise of Netscape Navigator that we no longer need to install software applications? Instead everything runs in “web browser” which is available on all platforms?

At present serving ~1 million annual readers (post pandemic the worldwide appetite for travel has increased significantly) with 15k project listings in nearly every country of world, 2.5k community contributed articles and 10gb of photos!

Where next? Multi-lingual offering allowing native speakers to read & publish in their regions language. Post-pandemic travel ban and listings, should be reviewed with heads up on which projects are active.

From content point of view we’d like to in better position to monetize our worldwide wandering team of content creators. On technical side we need additional contributor(s) to maintain, update & ultimately takeover.

One of the best aspects of this project is it actively inspired and encouraged other people around the world to participate in the idea of creating and offering meaningful educative cultural exchange travel projects.

#opensourcesoftware

#meaningfultravel

#volunteering

#worktheworld

#gapyear

#php #postgres #apache #nginx #linux #putty

#psid_my,—nvoice; bill?!

Categories
Uncategorized

WordPress CMS : Developer Notes

Working presently to migrate a large (2000+ pages, 10gb images) travel blog from a bespoke PHP CMS to WordPress.

Over last 15 years task of developing, updating and maintaining the handwritten CMS has become burdensome and time consuming.

There is a dependency on me as developer being only person who knows architecture / codebase.

Migrating to WordPress makes sense for a number of reasons – developers, administrators and content creators worldwide are familiar with the platform.

There is a healthy marketplace for commercial and freely available plugins, themes and tooling along with a choice of hosting providers.

WordPress is the internet’s most widely deployed CMS with estimated 65% market share

A few developer notes on implementation / architecture –

Posts / Pages

Aren’t these the same thing – content? Isn’t WordPress now a generalised content management platform rather than being specific to blogs?

Posts are displayed chronologically having URLs in form /<year>/<month>/<day>/<post-name> but otherwise posts and pages have the same a data model, right?

Shouldn’t tables post and page be merged into a new table “wp_content” – with content-type field to distinguish different page types?

Content Tree / URL Index

WordPress organises content hierarchically using parent – child database relation with each tier representing URL segments /<tier-1>/<tier-2>/…

This makes searching for content by URL relatively inefficient. Large sites might also encounter wp_posts.page_name namespace collisions

Rendering a content tree requires a recursive SQL or application code algorithm to “walk the tree”.

Why not decouple wp_posts.page_name and store as fully qualified URL in an index table?

                      Table "public.url_index"
   Column    |          Type          |  
-------------+------------------------+-----------+-----
 content_id  | integer                | 
 url         | character varying(256) | 

-- SQL to retrieve all pages under /blog/2023
select
 c.id,
 c.title,
 c.content,
 i.url
 from wp_content c, wp_url_index i
where c.id = i.content_id
and i.url like '/blog/2023%'
order by i.url asc, c.id asc

Parent / child relation is no longer required – by publishing a page under a specific URL parent hierarchy is implicit and can be determined simply be traversing URL segments.

Having a URL index table makes retrieving one or groups of pages very low cost and efficient as a b-tree index can be added to url_index.url

Database Optimisation

To my mind WordPress core requires only three tables and fields could be simplified significantly:

wp_content: <id>,<title>,<content>,<author-id>,<created-date>,<last-updated>, <content-type>, <version-id>
wp_metadata: <content-id>,<metadata-key>, <metadata-value>, <metadata-type>
wp_url_index: <content-id>,<url>

Metadata is a generalised key/value store linked to content by id.

Any structured metadata relating to a page can be maintained – Plain text (integers, floats (albeit as text), varchar) JSON, serialised PHP objects even JPEG encoded images.

Ok, lets consider a query for posts having extended metadata representing price from – to field:

select 
 mk.content_id,
 mvf.metadata_value,
 mvc.metadata_value,
from 
wp_metadata_key mk,
wp_metadata_value_float mvf,
wp_metadata_value_varchar mvc,
where mk.metadata-type = "price"
and mk.id = mvf.metadata-key-id 
and mk.id = mvc.metadata-key-id 
and mvf.metadata-value BETWEEN "30" AND "50"

Metadata in this scenario has container for varchar() but also one for float field enabling numerical queries.

Version ID

By maintaining version id field alongside content or metadata application can automatically determine appropriate implementation to retrieve, render or update.

Version ID beyond describing WordPress version number could also specify this metadata is stored as format serialised JSON or this is a serialised PHP 5 object – over time as application evolves different implementations can be supported even concurrently.

Conclusion

We appreciate making changes to any codebase having grown organically through time is not trivial – especially when a large market of 3rd party plugins / tools are based on current implementation and would potentially need to mirror changes in their own new versions.

Planning a smooth migration path is not an easy undertaking. But on the other side, optimisations at architecture level bring significant benefit increasing application performance, lowering computational resource costs, make application code easier to work with and allow the platform to scale efficiently.

Categories
Internet of Things sensors wearables

Wearables: Mi Band 6 Smart Fitness Tracker

Mi Band 6 is a wearable smart watch / fitness band tracker from Xiaomi with Mi Fit health mobile app.

For a time i’ve looked for an economic sport tracker band or smart watch that is waterproof, swim compatible, syncs with Strava and offers long battery life.

Crucially, what you don’t get with slim lightweight (12.8g) fitness band compared to smart watches from brands like Apple or Garmin is GPS location tracking.

To record route data you must also carry a paired GPS smart phone with Mi Fit app when cycling, running or walking.

Lack of GPS radio conversely helps preserve battery life promised as upto 14 days.

Timing, location and speed data recorded by Mi Fit appeared reasonable accurate for cycling, running & walking.

Strava is a social platform offering privacy options to obscure route start / end points, Mi Fit by default does not share route data.

GPS Elevation was not accurate beyond ~100 metre range. Multiple climbs / descents of between 20 and 150 metres were not tracked correctly (sea level promenade route displayed incorrectly as 90 metres).

Also, there is no indication from device sensors or mapping provider of incline to describe stage climb / descent.

Smart bands promise to remove need to carry a physical wallet and keys but at present Mi Band 6 only supports AliPay / WeChat which is not widely accepted in UK and there is no option to use Google or Apple Pay eWallet instead.

As a keyring it would be nice to use NFC to enter gym contactlessly. Instead of carrying seperate gym card, in theory leisure centre member app could put entry token into smart band keyring.

Who being unable to find coin or having lost key token wouldn’t rather tap smartband to open gym locker?

Mi Band 6 implements Bluetooth LE v5 but it isnt clear (without further investigation) what level of encryption and authentication are used, suggesting by default a users private biometric data could potentially be read without consent.

For secure e-payments having 2 factor authorisation, for example biometric fingerprint unlocking would prevent unauthorised use.

Onboard environmental sensing is dissapointing. Weather data appears to sync only occasionally from an online feed. It lacks an hourly local forecast and key metrics air pressure, humidity, wind speed / direction, time of sunrise / sunset and moonphase.

During sporting activities having a record of local environmental conditions from onboard temperature / humidity, barometric pressure and light level sensors combined with local forecast vastly improve data quality – full sun vs shade, hi/lo temperature, wind speed / direction affect performance.

Strava can be linked to Klimat.app to add generalised conditions to cycle rides although this is not segment specific.

Overall from amateur sports perspective rather than someone with medical background (and scientific tools to verify accuracy) Mi Band fitness health metric tracking & reporting seems impressive.

Key metrics missing from health monitoring include Blood Pressure (distinct from heartrate), breathing (respiration) rate and body temperature. Isn’t stress level dependent on these as inputs for meaningful recording?

Two minor issues, touch screen doesnt work correctly in water (swim activity should be triggered at poolside) and strap button can fly away when clipping / unclipping band, so care must be taken when removing band after swimming before entering Sauna / Steam Room (5 ATM waterproof rating excludes Spa use).

Swim tracking based on an indoor pool session is impressive, there is no need to enter pool length and distance, stroke rate and stroke classification appear accurate although Mi Fit does not display lap by lap breakdown.

For Gala’s or group training being able to contaclessly register participants bands with coach or club app would allow consolidation of timing and training data.

Also a lap timing stopwatch feature would be nice to have.

To record body weight (a key goal of most fitness programs) by default weight is manually input into Mi Fit.

Enter Xiaomi smart scales, a wireless Bluetooth enabled companion device designed (and found in practise) to sync simply and seamlessly with Mi Fit.

Mi Fit should allow users to set goals for weight loss (or gain) and show comparison to previous week / month / year.

Presently (Mi Fit v6 2.1) there doesn’t appear to be option to sync weight data to Mi Band 6.

Getting supplementary data into Mi Fit from other 3rd external sources, event timing systems, gym machines etc on completing a workout should be as easy as requesting / agreeing a Bluetooth data sync request.

Sadly no Life Fitness machines at local gym presently support Bluetooth and its not clear if Google Fit, Mi Fit or Strava have yet established an open standard interoperable and compatible with all bands, smartwatches and machine makers.

Motion sensor based activity auto detection: gesture recognition, counting and timing is not arguably without inaccuracies.

Considering underlying math complexity, long device battery life and low price point recognition of simple activities like rowing machine worked well. Yoga poses on other hand were not recognised in current version.

Behaviour Tagging feature suggests use of cutting edge AI Machine Learning – where user trains band for specific activity recognition / classification, resulting in personalised model and great accuracy for swim stroke or tennis shot recognition.

Overall Mi Band 6 has a winning low price point, impressive feature set, relatively good precision / tracking accuracy, brilliant sharp and bright OLED display and long battery life. Setup and sync with Mi Fit app and Xiaomi smart scales was simple and easy. Mi Fit dashboard app provides detailed and attractive graphical chart metrics. Finally, for fashion concious there’s a choice of inexpensive coloured and stylised replacement straps.

Xioami Mi Band 6 : from 2022 review of best waterproof swim fitness trackers:

https://www.coachmag.co.uk/fitness-trackers/6139/the-best-waterproof-fitness-trackers-for-swimmers

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) &amp; 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
arduino C/C++ Coding microcontrollers Software weather station

Sunrise / Sunset – Is Arduino ahead of Its Time?

Recently I added Sun Moon Library to my DIY Arduino Weather Station.

Given a location specified as Latitude / Longitude coordinates and a date / time this clever algorithm uses astronomical math routines to provide timing of sunrise, sunset and moon age.

https://www.flickr.com/photos/jannerboy62/31589567761/

In spite of Arduino ATMega328p chipset being only an 8 bit architecture and float data type being low precision ( 6 – 7 decimal digits ) timings output by sun moon are said to be accurate to second scale.

Comparing results with UK Met Office weather forecast during testing I found Arduino sunrise / sunset timings to be out, with a margin or error of around 15 minutes.

Being suspicious of data, one sunny afternoon I found a vantage point and watched sun disappear below horizon, timing the event.

Sure enough Arduino data was indeed inaccurate.

Arduino DIY Weather Station and sensors.

What could be the problem?

While my knowledge of maths is not sufficiently advanced to properly understand each line of Sun Moon library algorithm, to eliminate possibility of faulty code I decided to try another implementation, Dusk to Dawn.

After running a test for same location, result with new library continued to show an inaccuracy of 15 minutes.

Weather Station timing (date / time) is provided by a Real Time Clock (DS3132 module) which is synced from internet via Network Time Protocol (NTP). A quick check showed date and time to be correct, although manual correction for daylight savings time (DST) seemed sub-optimal.

This pointed to a coordinate error.

Google gives my location Bournemouth, UK as Longitude / Latitude 50.7192° N, 1.8808° W.

Google results for Latitude / Longitude Bournemouth, UK

These coordinates are defined in Weather Station code as:

// Lat/Long: Bournemouth 50.7192° N, 1.8808° W
#define LOC_latitude    50.7192
#define LOC_longtitude  1.8808

Here was the problem.

Checking cooordinates for another location, London, UK ( 51.5074° N, 0.1278° W ) Arduino gave sunrise / sunset with only a minor ( < 1 minute) difference in timing.

Bournemouth is West of Greenwich Meridian (zero line for Longitude) by approx 107 miles. Each degree of latitude is approximately 69 miles (111 kilometers) apart.

Google gives coordinates (50.7192° N, 1.8808° W) as decimal degrees and “N/S/E/W”, a human friendly representation.

Arduino code expects decimal degrees and Plus/minus symbol.

ISO 6709 International Standard defines Longitude as a number preceded by a sign character. A plus sign (+) denotes east longitude or the prime meridian, and a minus sign (-) denotes west longitude or 180° meridian (opposite of the prime meridian)

Similarly, according to Microsoft, “The latitude is preceded by a minus sign ( – ) if it is south of the equator (a positive number implies north)”.

Updating Weather Station to include latitude minus symbol, code now provided accurate timings –

// Lat/Long: Bournemouth 50.7192° N, 1.8808° W
#define LOC_latitude    50.7192
#define LOC_longtitude  -1.8808

In navigation, the 1 in 60 rule states that for each degree off (or displacement) over a distance of 60 nautical miles (NM), it will result in 1 NM off course

A trans Atlantic journey by boat from Southampton to New York ( 2974.5 nautical miles ), given a similar error in bearing of ~2 degrees might arrive in Boston or possibly Washington.

Sunrise / Sunset times on Arduino LCD Weather Station display

We were in agreement at last, today sunrise would occur at 06:25 and sunset at 19:46.

References:

https://github.com/steveio/arduino/blob/master/WeatherStation/WeatherStation.ino

https://en.wikipedia.org/wiki/ISO_6709#Longitude

https://docs.microsoft.com/en-us/previous-versions/mappoint/aa578799(v=msdn.10)?redirectedfrom=MSDN

https://stackoverflow.com/questions/51626412/getting-negative-values-of-latitude-and-longitude

https://www.metoffice.gov.uk/weather/forecast