Friday 11 December 2020

C# Threads and Events

Background

For those of you who remember Visual Basic, that was a time when you generally did not need to worry too much about threads and the user interface. When you pressed a button, the display would update based on what was happening (you might need to add some code to update the interface, but that was just a simple statement). Migrating to Visual Basic.NET came as a bit of a shock, when you clicked on a button, the code behind it hogged the resources and the display would freeze (and in extreme cases through up a warning about needing to be pumped or something).

This was because the user interface was under the programs control, and by default the thread that ran the UI was also the thread that ran the code behind your button.

This was fine for simple programs that did something with no user interaction (you set it up, pressed the go button, it did what it was supposed - sometimes - to do and returned a result). It was easier on the user than command line, but there was little feedback (no progress bars etc.).

For that you needed to venture into Threads. Threads appear to be similar to a processor running your code. You have the thread created to run your program, if it is busy calculating something, it does not have time to do anything else. If you want your user interface to show progress or allow you to stop your calculation, you need to create and run your calculation in another thread.

Originally Visual Studio provided a very limited framework to use threads - you could:

  • Create a thread based on a method on an instance of an object
  • Start a thread
  • Check if it was running

Everything else you had to roll yourself. This is where Events came in.

The Event is part of the Object Orientated Programming model. An object provides Events that other objects (code) can listen out for and respond to. It provides a richer collection of options than the Interrupt model used in single thread processors, not only signalling for an interaction but passing complex data. Additionally, multiple threads can listen for the same event.

In C# there is a slight problem - the user interface thread is solely responsible for (oddly enough) the user interface, but the event response is on a separate thread. This means that any events destined for the user interface has to be sent through a delegate which invokes the call on the user interface thread.

Thread handling has been improved over time, there are now background worker threads which handle a lot of the hard work involved but do not offer the full flexibility of controlling your threads and events directly.

The following describes a test program and the required code. Some of it could have been dealt with using background worker threads.

Form

The form uses the following controls:

  • •Tool Strip (located at the top)
  • •Add a Tool Strip label, set the text to “Threads”
  • •Add a Tool Strip Combo Box. Set the text to 1, add items (one per line) 1, 2, 5, 10
  • •Add a button to the Tool Strip, set the text to “Start” and the DisplayType to Text
  • •Status Strip (located at the bottom). Remember to set ShowItemToolTip to true
  • •Panel – located between the tool strip and the status strip. Set Dock to Fill.
  • •Split Container – added to the Panel.
  • •Text Box named txtMessage added to the right hand split panel, Multi-Line, Vertical Scroll Bar and Dock set to Fill


Code

using System;
using System.Threading;
using System.Windows.Forms;

namespace eventtest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        // Array for instances of the Worker class
        private Worker[] workers;

        // Array for the matching threads
        private Thread[] workerThreads;

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            // get the number of threads from the combo box 
            int threads = int.Parse(tsbThreads.Text);

            // Create the progress bars for the number of threads
            CreateProgress(threads);

            // Resize the arrays
            workers = new Worker[threads];
            workerThreads = new Thread[threads];

            // for each intended thread
            for (int ix = 0; ix < threads; ix++)
            {
                // Create a new Worker instance
                workers[ix] = new Worker(ix, 30);
                // Create a new thread
                workerThreads[ix] = new Thread(workers[ix].Start);

                // Connect the events to the event handlers
                workers[ix].ProcessProgress += HandleProgress;
                workers[ix].ProcessComplete += HandleComplete;
            }

            // Start each thread in turn
            for (int ix = 0; ix < threads; ix++)
            {
                workerThreads[ix].Start();
            }
        }

        // Event handler for a thread reaching completion
        private void HandleComplete(object sender, CompleteEventArgs e)
        {
            SetComplete(e);
        }

        // Delegate for SetComplete
        private delegate void SetCompleteDelegate(CompleteEventArgs e);

        // If the call to this method is not on the UI thread, invoke it 
        private void SetComplete(CompleteEventArgs e)
        {
            // Check if this call is not on the UI thread
            if (txtMessages.InvokeRequired)
            {
                // Invoke the delegate 
                var d = new SetCompleteDelegate(SetComplete);
                Invoke(d, new object[] { e });
            }
            else
            {
                // Add the message to the message text box
                txtMessages.Text = $"{txtMessages.Text}Thread {e.ID} {e.Message} {Environment.NewLine}";
            }
        }

        // Event handling for thread progress
        private void HandleProgress(object sender, ProgressEventArgs e)
        {
            SetProgress(e);
        }

        // Delegate for SetProgress
        private delegate void SetProgressDelegate(ProgressEventArgs e);

        // If the call to this method is not on the UI thread, invoke it 
        private void SetProgress(ProgressEventArgs e)
        {
            // Check if this call is not on the UI thread
            if (txtMessages.InvokeRequired)
            {
                // Invoke the delegate
                var d = new SetProgressDelegate(SetProgress);
                Invoke(d, new object[] { e });
            }
            else
            {
                // Find the progress trip for updating
                var x = (ToolStripProgressBar)statusStrip1.Items[$"Progress{e.ID.ToString("000")}"];
                // Set the percentage
                x.Value = e.Percentage;
                // Set the tool tip
                x.ToolTipText = e.Message;
            }
        }
            private void CreateProgress(int Threads)
        {
            // Clear all existing status strip items
            statusStrip1.Items.Clear();

            // For each thread
            for (int ix = 0; ix < Threads; ix++)
            {
                // Create a tool strip progress bar
                ToolStripProgressBar newItem = new ToolStripProgressBar($"Progress{ix.ToString("000")}");
                // Set the minimum and maximum values - this will be the percentage completion
                newItem.Minimum = 0;
                newItem.Maximum = 100;
                // Add the progress bar to the status strip
                statusStrip1.Items.Add(newItem);
            }
        }
    }

    // This class is a test class to show how progress and completion events can be passed to the UI
    public class Worker
    {
        // Thread ID
        private int ID;

        // Count items
        private int Things;
        public Worker(int id, int things)
        {
            ID = id;
            Things = things;
        }

        // Start the processing
        public void Start()
        {
            // This example just runs through a number of cycles with a set period wait
            // and raises an event each cycle showing the progress
            for (int ix = 1; ix <= Things; ix++)
            {
                Thread.Sleep(500);

                // Raise an event
                Progress(new ProgressEventArgs(ID, (ix*100)/Things, $"{(ix * 100) / Things}%"));
            }
            // Raise an event indicating the thread has completed
            Complete(new CompleteEventArgs(ID, "Process complete"));
        }

        // Raise an event showing progress, passing a ProgressEventArgs object
        protected virtual void Progress(ProgressEventArgs e)
        {
            EventHandler<ProgressEventArgs> handler = ProcessProgress;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        // Raise an event indicating the process has completed
        protected void Complete(CompleteEventArgs e)
        {
            EventHandler<CompleteEventArgs> handler = ProcessComplete;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        // Public event definition
        public event EventHandler<ProgressEventArgs> ProcessProgress;
        public event EventHandler<CompleteEventArgs> ProcessComplete;

    }

    // Process completion argument object
    public class CompleteEventArgs : EventArgs
    {
        // Thread ID raising the event
        public int ID;
        // Completion message
        public string Message;

        // Constructor
        public CompleteEventArgs(int id, string message)
        {
            ID = id;
            Message = message;
        }
    }

    // Process Progress argument object
    public class ProgressEventArgs : EventArgs
    {
        // Thread ID that raised the event
        public int ID;
        // Process percentage
        public int Percentage { get; set; }
        // Process message
        public string Message { get; set; }

        // Constructor for progress argument
        public ProgressEventArgs(int id,int percentage, string message)
        {
            ID = id;
            Percentage = percentage;
            Message = message;
        }
    }
}

Results

When the program runs, the form is displayed. Select the number of threads from the drop down list (or type it in) Click on Start.


This program is running with five threads. There are five progress bars, and the hover over tells you the percentage. Note that the form needed to be widened to display the five progress bars, they have a default width and the original width only displays four of the bars.

Setting the progress bars width such that all are displayed is left as an exercise for the reader (remember the bars will need to be resized if the enclosing form is resized).


Sunday 27 September 2020

Microsoft SQL Server – first steps

Set Up

A free Developer edition of Microsoft SQL server is available from the Microsoft site.

As Microsoft change their site and methods regularly, it is probably best to follow their instructions.

It is worth getting the Microsoft SQL Management Studio at the same time as it does make a lot of the set up a lot easier.

The following will assume you have Visual Studio 2019 (Developer edition), SQL Server Express and the SQL Server Management Studio installed.

For simplicity, it is assumed that you are logged in with Windows Authentication, if not, you will need to add username and password to the database to allow access.

Also, ensure that you have set the System Administrator password in SQLServer.

My First Database

Just to be clear, this is not setting up a production database, and certainly not one ready to face a hostile world.

Start the SQL Server Management Studio (SSMS from here).

The object explorer on the left-hand side shows what it can see. 

Select the Databases node. If this is your first database, you will probably find that there is just a System Databases node inside.

You can create a database simply by right clicking on Databases and using the defaults.

This is not how databases should be created, you should have a plan written down and decide where all the files should be located. You should also consider how big the database will be initially and how the database is going to grow. Does the database need to have an upper size limit and when it grows, how much will it grow by. These can have a dramatic effect on performance (and also identify when there is a problem).

However, for this case, the defaults will be fine. Add your user as a SQLServer user via permissions – we will use Integrated Security later – this means your login is used to log in to the database.

My First Table

Efficient table design is important, you should as part of your design have considered what data you are storing, and how the data will accessed, updated and possibly deleted.

There is a process called data normalisation – for efficient storage, you should only store the information once, however it might be that in normal usage, you need certain pieces of data together, so it might be worth keeping them in the same table.

This is just about setting up a simple table, so we will initially use the SSMS to create the table.

If you click on the node for the database you created earlier it will display various nodes such as Tables, Views, Programmability etc.

Clicking on the Tables node will show you the System Tables node and possibly a File Tables node. Right click on the Tables node and select Tables.

The first data column will be an ID or Identity column. This will be an Integer (sufficient for this example). In the properties you want it not to be able to be Null, and you need to scroll down to the Identity Specification. Set that to Yes and then you can set the Is Identity field to Yes.

An Identity column is automatically filled with a value that is incremented (by default by one – there is a setting for that). It can be used to Identify the row.

The next column (Name) is a VARCHAR. This will default to 50, but 20 will be sufficient. Set that to not Null as well.

The last column is Description. This is a Text column (the contents of Text columns are not stored in the database table, there is a managed table that holds the text information. This makes the text column type efficient to store large amounts of text but at the disadvantage that the database has to be accessed at least twice, – first to get the original row that contains the reference, and second to get the text).

Viewing your data

For this, you can create a new Query Window in SSMS.

The following SQL statement will select all the contents of the table SimpleNameDescription owned by DBO in the database Testdatabase.

select * from Testdatabase.dbo.SimpleNameDescription;

Disappointingly there is no data in the database at the moment.

That is easily fixed

My First Row

Open another Query window and use the following SQL statement:

insert into [dbo].[SimpleNameDescription] ([name],[description]) values ('First one','This is the first record');

This inserts a record into the database table, setting the fields name and description to have the values listed. Note there is no mention of the ID field. The database, table and field names are enclosed in square brackets to identify they are database, table and field names.

Run the select statement from earlier (that is why a second query window was used for the insert).

Note the ID is 1. When you insert the next record, the ID will be 2. This is the great advantage of using an identity, it handles the value itself.

Using the same insert statement, add a couple of additional rows and then use the select statement to view your handiwork.

Changing a row

You have added a number of rows, but you made a mistake on one of them, how do you fix it?

SQL statements can have a Where Clause, this can be used to identify a subset of the rows in the table. Now luckily (or by good design) we have a column that identifies the rows, so we can update a specific row.

UPDATE [dbo].[SimpleNameDescription]   SET [Description] = 'new description' WHERE id = 2;

This will change the description on the row with ID of 2.

Deleting a row

Add a row using:

insert into [dbo].[SimpleNameDescription] ([name],[description]) values ('rubbish','This is rubbish');

Now as it says, this is rubbish, so you want to remove it. Use the select statement above to find the ID number

Open another query window

And add the following:

DELETE FROM [dbo].[SimpleNameDescription] WHERE id = 3;

The value 3 needs to be replaced with the ID of the row of the row you want to delete.

Run the SQL and then run the select. That row has gone. Note, that if you run the delete without the Where clause, it will delete everything and unless you have backed it up, it is gone for good. Be warned.


Programmability

Stored procedures

It is possible to build incredibly complicated systems using the SQL statements, but it has a number of security and maintenance issues. This is where Stored Procedures come in handy. These are SQL statements that can be reused.

This is a simple Stored Procedure:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE Test @ID int
AS
BEGIN
select * from Testdatabase.dbo.SimpleNameDescription where id=@id;
END
GO

To execute the stored procedure open another query window:

USE [Testdatabase]
GO
[dbo].Test   2;
GO

Accessing the database in C#

The following assumes you have logged in via Windows Authentication and your Windows Login has access to the database.

First you need to determine your SQL log in string.

string connectionString= "Server=localhost\\SQLExpress;database=Testdatabase;Integrated Security=true;;";

The SQL server name is your machine (localhost) and is called SQLExpress. The database is named, and it states that it will use Integrated Security. The latter hands off the heavy lifting of user names and passwords to the Windows Login. If you need to use username and passwords, replace the Integrated security with 

user Id=UserName; Password=Secret;

The overall code is:

       static void Main(string[] args)
        {
            Console.WriteLine("Connecting...");
            string connectionString= "Server=localhost\\SQLExpress;database=Testdatabase;Integrated Security=true;;";
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                string sql = " SELECT *  FROM [dbo].[SimpleNameDescription]";
                SqlCommand command = new SqlCommand(sql, connection);
                connection.Open();
                SqlDataReader reader = command.ExecuteReader();
                while (reader.Read())
                {
                    Console.WriteLine($"{reader.GetInt32(0)} {reader.GetString(1)} - {reader.GetString(2)}");
                }
                reader.Close();
            }
            Console.Write("Press any key");
            Console.ReadKey();
        }

This will display all the rows in the table SimpleNameDescription.

Now that is fine if you want to execute non selective command, but what if you want to only read for a particular ID?

 SQL Command Parameters

Though it is possible to cobble together your SQL statement, it is better to use parameters.

        static void test2(int id)
        {
            Console.WriteLine("Connecting...");
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                string sql = " SELECT *  FROM [dbo].[SimpleNameDescription] where id=@id";
                SqlCommand command = new SqlCommand(sql, connection);
                command.Parameters.AddWithValue("@ID", id);
                connection.Open();
                SqlDataReader reader = command.ExecuteReader();
                while (reader.Read())
                {
                    Console.WriteLine($"{reader.GetInt32(0)} {reader.GetString(1)} - {reader.GetString(2)}");
                }
                reader.Close();
            }
        }

Of course, as mentioned above, it is better to use stored procedures.

It is important to set the CommandType, otherwise the stored procedure will not be able to see the 

        static void test3(int id)
        {
            Console.WriteLine("Connecting...");
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                string sql = "[dbo].[Test]";
                SqlCommand command = new SqlCommand(sql, connection);
                command.CommandType = CommandType.StoredProcedure;
                command.Parameters.AddWithValue("@ID", id);
                connection.Open();
                SqlDataReader reader = command.ExecuteReader();
                while (reader.Read())
                {
                    Console.WriteLine($"{reader.GetInt32(0)} {reader.GetString(1)} - {reader.GetString(2)}");
                }
                reader.Close();
            }
        }

References

https://www.microsoft.com/en-gb/sql-server/sql-server-downloads

https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqldatareader?view=netframework-4.7.2

https://www.connectionstrings.com/sqlconnection/

https://stackoverflow.com/questions/12220865/connecting-to-local-sql-server-database-using-c-sharp


Sunday 5 July 2020

Reading USB (Serial) data

Most microcontrollers have an option to output data over the USB link.

It is helpful during development to be able to read state information and other values that allow the developer to see what is happening.

The first thing is to identify which USB port is in use.
            foreach (string port in ports)
            {
                Console.WriteLine(port);
            }
For my set up, COM5 was the one in use.
using (var sp = new System.IO.Ports.SerialPort("COM5", 115200, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One))
            {
                Console.WriteLine("Reading serial port");
                sp.Open();
                while (true)
                {
                    var readData = sp.ReadLine();
                    Console.WriteLine($"[{readData}]");
                }
            }
This will display on the console anything output from the device.





Sunday 14 June 2020

BBC Micro:Bit Menu system (with persistent choice)

The BBC Micro:Bit is a simple microcontroller with a 5 x 5 matrix of LEDs, two buttons, 3D magnetic and acceleration sensors and a CPU temperature sensor.
The two buttons can make complicated interfaces rather difficult, however it is possible to build one.
One aadditional feature of this menu system is that your choices are persistent. If you select menu item, power off the Micro:Bit then subsequently power it back one, it will remember the choice (subject to it not being reflashed of course).

Code

This was written in Micropython using the Mu editor.
from microbit import *
import os
import utime
menuitem = 0
if 'choice.opt' in os.listdir():
    with open('choice.opt') as choice:
        menuitem = int(choice.read())
display.scroll("Menu item" + str(menuitem))
start = utime.ticks_ms()+6000
interval = 2000
while True:
    now = utime.ticks_ms()
    if start > now or now - start > interval:
        if menuitem == 0:
            temp = temperature()
            interval = 2000
            display.scroll(str(temp) + 'C', delay=100, wait=False)
        elif menuitem == 1:
            temp = temperature()
            interval = 2000
            display.scroll(str(temp + 273.15) + 'K', delay=100, wait=False)
        elif menuitem == 2:
            level = display.read_light_level()
            interval = 2000
            display.scroll(str(level) + ' light', delay=100, wait=False)
        elif menuitem == 3:
            level = compass.get_field_strength()
            interval = 6000
            display.scroll(str(level) + ' nTesla', delay=100, wait=False)
        elif menuitem == 4:
            display.scroll("Menu test")
        start = now 
    if button_a.is_pressed():
        display.scroll("Menu", delay = 100)
        sleep(50)
        while not button_a.is_pressed():
            display.set_pixel(4,menuitem,5)
            if button_b.is_pressed():
                display.set_pixel(4,menuitem,0)
                menuitem = menuitem + 1
                if menuitem > 4:
                    menuitem = 0
                with open('choice.opt','w') as choice:
                    choice.write(str(menuitem))
                display.scroll("Menu item" + str(menuitem), delay = 100)
            sleep(100)
            display.set_pixel(4,menuitem,5)
            sleep(100) 
    sleep(400)
The persistent choice is handled by this code:
menuitem = 0
if 'choice.opt' in os.listdir():
    with open('choice.opt') as choice:
        menuitem = int(choice.read())
The menu item is given a default value (0).
The file 'choice.opt' is checked if it exists in the directory list, if it is, then the value of the menu item is read from the file and assigned to the menuitem variable. When a subsequent decision is made to change the menuitem, this value is written out to the file, making it available the next time the Micro:Bit is switched on.

The main loop is entered after the start variable is set in advance of the current tick count and the display interval is set (strictly speaking the interval should be dependent on the menu choice but it only affects the first cycle).

Each loop, if the difference between the ticks now and the (loop) start ticks is greater than the interval, then the menuitem is used to choose what to do.
In this example it is used to choose which sensor is read and the results displayed.
Menu choices are:

  1. Temperature in degrees Celsius.
  2. Temperature in Kelvin
  3. Light level (based on the light falling on the LED matrix)
  4. Magnetic field strength in nanoTesla (using the compass module)
  5. A message.
The first three keep the interval at two seconds, but the magnetic field strength is a longer piece of text, so that is stretched to six seconds by setting the interval.
The Start ticks value is set to the Now value.

The next part of the code checks for the A button (left side) being pressed.
If so, it then loops until the button is pressed again.
Inside that loop, pressing the B (right hand) button increments the menuitem value, writes it to the file and shows a pixel on the right hand column indication which option is currently chosen.
Pressing button A exits the loop and recommences the outer infinite loop.

Disadvantages

This does mean that during normal operation, button A is not available. This might not be an issue but is something to bear in mind.

References


Saturday 13 June 2020

Pimoroni Envirobit

Pimoroni Envirobit


The Pimoroni Envirobit is a set of sensors for the BBC Micro:Bit .


As you can see, it is equipped with a slot to take the Micro:Bit, so no soldering is required.

The Envirobit is fitted with the following sensors:

  • BME280 environmental sensor - which measures temperature, pressure, humidity and can calculate the altitude based on a supplied base pressure level (discuss).
  • tcs3472 RGB sensor - which measures Red Green and Blue light levels as well as “white” light levels. Also includes two illuminating (white) LEDs,
  • Sound - a small microphone allows the sound level to be measured on one of the Micro:Bit’s analogue pins

Assembly

Assembly is simple. Take the Envirobit board with the sensors facing forward, and insert the Micro:Bit with the LEDs also facing forward.
Due to the nature of the connection, you can swap the Microbits if the colour scheme does not match your needs.

Software

The main software support for the Envirobit is orientated towards the Microsoft MakeCode block based system.
There is some support for MicroPython. There is a GitHub link here: https://github.com/pimoroni/micropython-envirobit

There are three python files in the Library.
  • sound.py
  • bme280.py
  • tcs3472.py

The files can be transferred to your Micro:Bit using the Files function in Mu.

Sound

Contrary to the description on GitHub, this is not a class, just three methods.

  • sound.read() - This takes a reading of the sound level and returns a value between 0 and 440. There is an offset value in the code to set the minimum sensitivity.
  • sound.wait_for_double_clap() - listen for two high level sound events in a second, returns True if detected
  • sound.wait_for_clap() - listen for a single high sound level event in a second, returns True if detected

tcs3472

This uses a class to access the TCS3472 sensor via I2C.
To use the sensor, import the module (having transferred it to the Micro:Bit) and instantiate an instance.
import tcs3472
light_sensor = tcs3472.tcs3472() 
Methods:

  • r, g, b = light_sensor.rgb() - returns a tuple of the corrected levels of red, green and blue out of 255
  • r, g, b = light_sensor.scaled() - return a tuple of the amounts of red, green and blue on a scale of 0-1
  • level = light_sensor.light() - return a raw reading of light level on a scale of 0-65535
  • light_sensor.set_leds(0) - Turn the LEDs off
  • light_sensor.set_leds(1) - Turn the LEDs on

BME280

This uses a class to access the BME280 sensor via I2C.
The instructions on GitHub are incorrect, there is a missing () on the end of the class instantiation. Python can be very unforgiving if you make a mistake of this kind.
import bme280
bme = bme280.bme280()

The bme280 class has the following methods:

  • temp = bme.temperature() - return the temperature in degrees C
  • pressure = bme.pressure() - return the pressure in hectopascals
  • humidity = bme.humidity() - return the relative humidity in %
  • alt = bme.altitude() - return the altitude in feet, calculated against the current QNH value
  • bme.set_qnh(value) - set the QNH value for calculating altitude

QNH is the atmospheric pressure adjusted to sea level (what the pressure sensor should read at sea level).
https://en.wikipedia.org/wiki/QNH

References

https://github.com/pimoroni/micropython-envirobit
https://en.wikipedia.org/wiki/QNH


Thursday 28 May 2020

Inkscape - SVG to PNG conversion

Inkscape is an Open Source vector graphics editing application.
The primary file type  is the Scalable Vector Graphics format. Modern browsers support the format.

However, there are still applications where bit map or raster graphic formats are required (Microsoft Azure Marketplace still wants specific resolution PNG files). Inkscape is a useful tool to convert the SVG format files to PNG.

Inkscape is in the process of rolling out version 1.0 which changes the command line parameters - so check your version.

Installation

Inkscape is available via the Raspbian repository.
After updating and upgrading the operating system, use:
sudo apt-get install inkscape

You can check the installed version.
inkscape -V
Inkscape 0.92.1 r15371

Command line usage

Inkscape has an extensive GUI, but it can also be used via command line. This is particularly useful if you want to convert an SVG file to a number of different sizes.

You can see the full list of options by using:
inkscape -?
Or a concise list with:
inkscape --usage
The command line options used:
-z No GUI
-w Width
-h Height
-e Export file name

Example
inkscape -z -w 1024 -h 1024 picture.svg -e picture.png
WARNING: unknown type: svg:foreignObject
WARNING: unknown type: i:pgf
Background RRGGBBAA: ffffff00
Area 0:0:85.039:85.039 exported to 1024 x 1024 pixels (1155.99 dpi)
Bitmap saved as: picture.png

References

https://inkscape.org/
https://en.wikipedia.org/wiki/Inkscape
https://en.wikipedia.org/wiki/Scalable_Vector_Graphics
https://inkscape.org/doc/inkscape-man.html


Monday 25 May 2020

Setting a default screen size for a headless Raspberry PI

I have a few Raspberry Pis, and most of them are run "headless" (without keyboard or display).

Now the default screen size for a headless PI is what used to be called VGA and has a resolution of 640 x 480. This was state of the art in the 'nineties, but unfortunately the majority of software expects a larger display. This means that programs tend to fall off the available screen size, in some case losing the close button as well.

You can set the display resolution via the Raspberry Pi configuration tool or via the command line using raspi-config. The problem is that there is no guarantee that your chosen display mode will then work with your monitor.

This particuklar resolution (720p) works okay on monitors and via VNC. Other options are available.

This can be set by editing the config.txt file

sudo nano /boot/config.txt

Scroll down to this section (the highlighted text areas will probably read VGA and 1 respectively). Change the mode to 4 and the resolution description - that is for your information later).
# uncomment to force a specific HDMI mode (this will force 720p resolution)hdmi_group=1hdmi_mode=4
Use Control X, Y to save and accept the default file name to save the changes.

Reboot your machine and you should have a much more workable graphics screen on VNC.

References

Sunday 24 May 2020

Micro:bit Beacon - part 1: the beacon

One of the features of the Micro:Bit is its Bluetooth compatible radio.

Unfortunately, it is not available in Micropython due to the size of the Bluetooth software stack.

Whatt is available is a Radio object which uses the radio hardware to communicate between Micro:bits.

There is a neat program on the documentation site called Firefly. This has groups of Micro:bits communicating with each other.

Now this project uses the "flash" technique from the Firefly project.

from microbit import *
import radio
import utime
import machine

# "Flash" effect from the Firefly program
#https://microbit-micropython.readthedocs.io/en/latest/tutorials/radio.html?highlight=Firefly#fireflies
flash = [Image().invert()*(i/9) for i in range(9, -1, -1)]

# Obtain the machine id
machineID=machine.unique_id()

# Flash the display over a half second 
# and send the machine ID every second.
while True:
        display.show(flash, delay=50, wait=False)
        radio.on()
        radio.send(str(machineID))
        sleep(500)
        radio.off()
        sleep(500)


Sunday 3 May 2020

Chaos - and the Headless Raspberry Pi

Cryptography is a voracious consumer of randomness, and computers depend on cryptography for security. Most operating systems harvest this randomness (under the name of entropy) from various pieces of hardware, quite often from mouse movement or keyboard operation. This is fine when the computer has a keyboard and/or mouse, but what happens when the computer is running headless?

Raspberry Pis quite often run headless, and remote operation using products like RealVNC encrypt the communication between the server and the client. To maintain security, no connection can be made until there is sufficient stored entropy. This can lead to long delays between switching the computer on and being able to connect to it remotely.

The package haveged is available in Debian based systems (including Raspbian and Ubuntu) and fills the entropy pool when the supply falls below a set value. This will speed up the start up of RealVNC.

Installing haveged

sudo apt-get install haveged

References


My Raspberry Pi collection

This is my collection of Raspberry Pis.

None are retired, some see more use than others.

My First Raspberry Pi

This was bought in a sale at Maplins in (I think) 2013. It was in a deluxe starter set with a case, a wireless keyboard with track pad, a WiFi dongle, powered hub and power supply. I bemused the member of staff by checking the Raspberry Pi it was made in the UK.
The Raspberry Pi (1) required a USB WiFi dongle (visible in the photograph above).

It is currently in use as a command line only MariaDB SQL server, though there are a number of other SD cards for it in the SD Card box.

Second Raspberry Pi

This was another sale item from Maplins. This starter set was the base level one that had a conventional wired keyboard and mouse, WiFi dongle, powered hub and power supply.

I also bought another case, similar to the one above.

Later I bought a number of add on boards that I could not get into the case. Maplins (again) had the Pimoroni PiBow cases, and so I bought one of those.

They are made of laser cut acrylic slices that you stack on top of each other and the board is held securely inside). As supplied, there was a clear panel over the top of the board, but by shuffling the top panel to the bottom, you can have an open top that allows access to the GPIO.
This side shot shows the slices, and you can see the intended top slice on the bottom. The pattern visible on the clear (ex top)  slice is where the various access ports and grill are visible.
Pimoroni now do a range of cases without the top panel, the Breakout Garden Pi Zero makes use of one of them.

This one is used where I want to access the GPIO pins directly. It has been used on the Raspberry Pi Foundation Physical Computing course and measuring temperature using a DS18D20 sensor.

Raspberry Pi 3

This was bought in WH Smiths of all places (and I also bought the camera), the bonus was the I had a discount card! It has an Official Raspberry Pi case.
The case allows some access to the GPIO, but a number of HATS interfere with the support pillar visible in the photograph below.
This is a most travelled Raspberry Pi, it gets to go on holiday (I have an micro SD card with various podcasts and iPlayer programmes. It also allows me to back up photographs if required).

It normally has my development micro SD in place, with all the languages and tools I use.

The OctoCam

 It comprises a Raspberry Pi Zero W, a 5Mp camera, Mini HDMI to HDMI cable, an On The Go cable and optional headers and stickers.
This uses the MotionEyeOS to function as a web cam.

Breakout Garden Pi

This is detailed here.

This normally runs as a temperature (plus humidity and pressure) display using a BME280 breakout board with the results displayed on the 1.12" screen.

Raspberry Pi Zero W

This Raspberry Pi Zero W is in an Official case and uses the top plate with the camera mount.

The camera was bought at the same time as the Raspberry Pi 3. Fitted in the Zero case provides a suitable housing. It is part of a project to scan ISBN numbers (and will be powered by a USB battery pack).

Pirate Audio headphones' amp

This uses a Pi Zero with pre-soldered header and the board to produce a music server.
As I have a large number of CDs, it also incorporates python software to copy files from CDs.
Details of the project here.

Raspberry Pi 4

The Raspberry Pi 4 is in a Pimoroni Heat Sink case. It uses micro HDMI sockets and is powered via a USB C socket. It is currently due to take over the development Raspberry Pi slot.

Saturday 2 May 2020

Raspberry Pi 4 and the Pimoroni Heatsink case

The Raspberry Pi 4 was released in June 2019, but I have only just bought my first one. When released, there was a 1GB, 2GB and a 4GB version. The 1GB version has been discontinued for consumer purchase, but in line with the Raspberry Pi Foundation's long term support ideals will be available for commercial purchase while there is still demand.
The board is supplied in a standard cardboard box with a (very brief) quick start and be careful guide.
I forgot to photograph the board before assembling it into its shiny new case. Maybe next time.

I bought my Raspberry Pi 4 from Pimoroni, and I also bought their bright and shiny Heatsink case.

Before assembling the board into the case, I installed the latest version of Buster onto an SD card using the Raspberry Pi Installer. This was then inserted into the board.

It was at this point I realised that I had forgotten two things.
The Raspberry Pi 4 is a lot more power hungry (hence my choice of the heatsink case) and so has moved away from the traditional micro USB power connector to the USB C type connector.

The second is that to make use of the dual 4K screen option, the monitor connection(s) use the Micro-HDMI connector.

There is a dedicated USB-C power supply available, and I probably should have ordered one (there is also a micro USB to USB-C adaptor). However, I ended up buying a 1m USB-C cable from my local supermarket.

The micro-HDMI cable would have been a bigger issue, but it turned out I had one in my cable collection.

Assembly of the case was straightforward. There is a tutorial here.

The only problem I had was with the thermal conductive pad. When I came to remove the backing sheet, it managed to tear and ended up looking like a blob of blue tack. Luckily there is a spare one which came in handy. So when fitting the thermal conductive pad, be very careful.

Here is the Raspberry Pi securely fitted into its case.

As you can see, the network port and the USB connectors are exposed, not just the sockets. The case will keep fingers away from the delicate components but it does not protect against much else.

There is a slot for the screen out cable, and the camera cable. The power over Ethernet port is also exposed.
The GPIO is available. It is claimed you could just plug a HAT in as normal, but it is recommended that you use a  booster header to raise the HAT well clear of the case to prevent any chance of it contacting the case and causing a short.
Side view showing the GPIO pins.
End view showing the USB 3 and USB 2 sockets and the higher speed Ethernet connection.
The other side view, showing the USB-C power socket, two Micro HDMI sockets for dual monitors and the composite/headphone socket.
The other end view showing the micro SD card and the camera connector.
If you have the option, it is well worth having your Raspberry Pi plugged connected via Ethernet. It makes the initial set up so much faster.

The Raspberry Pi 4 require Raspbian Buster or higher. The default behaviour appears to be different to previous builds. Previously, if you started a RaspberryPi up without a monitor, it would (by default) have a (virtual) GUI screen with what looked like VGA resolution. If you were using it with RealVNC to operate it remotely, this would be annoying as the resolution was not compatible with most of the applications etc. With Buster, no GUI screen (virtual or otherwise) is generated. If you plug a monitor in later, you just get a blank screen. This is annoying.

The solution is to set a default screen size (be careful that this screen resolution is actually supported by your monitor.

This can be done via the command line using:
sudo raspi-config