I2C1_Wr / I2C1_Rd functions hang with MikroC for PIC – Need Timeout

In some cases both the MikroC i2c functions I2C1_Wr() and I2C1_Rd() can hang or lockup indefinitely until the PIC is reset. This can happen if the I2C bus isn’t properly terminated, for example if a connection breaks or similar (broken wire or solder joint etc). Having your PIC lockup during operation is generally not very desirable if your are trying to develop a stable software system – if for some reason an i2c device hasn’t been properly connected your system will just lockup for good!

Different posts discuss this problem and its various causes:

https://forum.mikroe.com/viewtopic.php?f=13&t=21270&sid=f45f0199695794c83516f89919183cbb&start=0

and

https://forum.mikroe.com/viewtopic.php?f=88&t=60224

MikroC provided the code to their functions so that software developers (us) could work around the problem, this library implements versions of I2C1_Wr() and I2C1_Rd() that timeout rather than hang, a timeout values is passed as a parameter:

https://libstock.mikroe.com/projects/view/1052/i2c-non-blocking

The library doesn’t implement a C version, so inspired by it (thanks Danny!) I have ported the functions into C, and added some comments – use at your own risk, it works well for me on PIC18F family chips but hasn’t been exhaustively tested!

I have declared the timeouts as constants rather than allowing the timeout to be passed in as a parameter – this is so that the functions can be used as drop-in replacements without having to modify client code.

#define I2C_WRITE_TIMEOUT_US 200
unsigned short tI2C1_Wr(unsigned short d) {
    const unsigned int delay = 2; // us
    unsigned int max_retry = I2C_WRITE_TIMEOUT_US / delay;
    unsigned int retry;
    
    if (max_retry == 0)
        max_retry = 1;
        
    // Interrupt Flag bit - Waiting to transmit/receive
    PIR1.SSP1IF = 0;
    
    // Set data for transmission
    SSP1BUF = (unsigned char)d;
    // Wait for transmission to complete
    // 1 = Transmit is in progress
    // 0 = Transmit is not in progress
    //
    retry = max_retry;
    while (SSP1STAT.r_not_w == 1 && --retry > 0)
        delay_us(delay);
    // Timed-out stop transfer and return error
    if (SSP1STAT.r_not_w == 1) {
        // Enable Stop Condition
        SSP1CON2.PEN = 1;
        return 1;
    }
    
    // Wait for completion
    // 1 = The transmission/reception is complete (must be cleared by software)
    // 0 = Waiting to transmit/receive
    //
    retry = max_retry;
    while (PIR1.SSP1IF == 0 && --retry > 0)
        delay_us(delay);
    // Timed-out stop transfer and return error
    if (PIR1.SSP1IF == 0) {
        // Enable Stop Condition
        SSP1CON2.PEN = 1;
        return 1;
    }
    // Check that we got an ACK
    // 1 = Acknowledge was not received
    // 0 = Acknowledge was received
    //
    if (SSP1CON2.ACKSTAT != 0) {
        // No ACK, abort
        // Enable Stop Condition
        SSP1CON2.PEN = 1;
        return 1;
    }
    // All good...
    return 0;
}

And to Read:

#define I2C_READ_TIMEOUT_US 200
unsigned short tI2C1_Rd(unsigned short ack) {
    unsigned short d = 0;
    const unsigned int delay = 2; // us
    unsigned int max_retry = I2C_READ_TIMEOUT_US / delay;
    unsigned int retry;
    if (max_retry == 0)
        max_retry = 1;
        
    // Interrupt Flag bit - Waiting to transmit/receive
    // 1 = The transmission/reception is complete (must be cleared by software)
    // 0 = Waiting to transmit/receive
    //
    PIR1.SSP1IF = 0;
    // Set receive mode
    // 1 = Enables Receive mode for I2C
    // 0 = Receive idle
    //
    SSP1CON2.RCEN = 1;
    // Wait for read completion
    // 1 = The transmission/reception is complete (must be cleared by software)
    // 0 = Waiting to transmit/receive
    //
    retry = max_retry;
    while (PIR1.SSP1IF == 0 && --retry > 0)
        delay_us(delay);
    // Still not complete, get out...
    if (PIR1.SSP1IF == 0)
        return 0;
    // grab the data
    d = (unsigned short)SSPBUF;
    // ACK required?
    if (ack == 0) {
        // No
        // 1 = Not Acknowledge
        // 0 = Acknowledge
        //
        SSP1CON2.ACKDT = 1;
    } else {
        // Yes
        SSP1CON2.ACKDT = 0;
    }
    // Interrupt Flag bit - Waiting to transmit/receive
    // 1 = The transmission/reception is complete (must be cleared by software)
    // 0 = Waiting to transmit/receive
    //
    PIR1.SSP1IF = 0;
    // Start Ack sequence
    // 1 = Initiate Acknowledge sequence on SDAx and SCLx pins, and transmit ACKDT data bit. Automatically cleared by hardware.
    // 0 = Acknowledge sequence idle
    //
    SSP1CON2.ACKEN = 1;
    // Wait for completion of ack sequence
    retry = max_retry;
    while (PIR1.SSP1IF == 0 && --retry > 0)
        delay_us(delay);
    return d;
}

This works for the first i2c device, it is left as an exercise for the reader to convert it for a second i2c device, i.e. I2C2_Wr() and I2C2_Rd()

libpng – Changing compression level when writing PNG Image

This took me a bit of time to find, but I have been experimenting with libpng for writing software to save 16bit grayscale images and wanted to switch off compression to see if libpng would save the images out any faster than it normally does. When you call png_set_IHDR() you have to set the compression type to PNG_COMPRESSION_TYPE_DEFAULT, there is no alternative (e.g. there is no value like PNG_COMPRESSION_TYPE_NONE), this threw me for a bit…

Anyway after a spell of RTFMing, I eventually found out that you can alter then compression level, and indeed switch it off via a call to:

//
png_set_compression_level()
//

Setting a value of 0 in your code switches off compression, while setting it to 9 causes the lib to use maximum compression. Quick testing revealed that the time taken to save a PNG does decrease as you reduce this value towards 0 (and the saved image size increases!).

PNG is a good file format option for saving 16bit images, it is lossless and faithfully represents grayscale rather than fudging it into an RGB structure, and as it turns out, supports adjustable compression levels. As more cameras natively support higher bit-depth images, formats and software tools that support the greater precision will become more important so that Computer Vision and Machine Vision software can take full advantage.

PS Viewing 16bit grayscale images is harder than you might think, most software tools seem to down-sample them to 8bit grayscale, So far, I have found that good options are a.) ImageJ b.) Gimp 2.9 (not yet released, have to use a development build). These tools allow you to view the images and properly sample the 16bit pixel values etc…

Cross compiling libEXIF for ARM on Ubutnu

libexif is a software library that allows you to add EXIF tags to JPEG images, for example when saving JPEG images via libJPEG.

The following is a log of the steps taken to cross compile libexif on ubuntu for an ARM IoT device, you will need the arm-linux-gnueabi tool-chain installed on your build machine:

1.) Download the latest libexif source code from:

http://sourceforge.net/projects/libexif/files/latest/download?source=files

2.) uzip & untar the software:

3.) Execute the following from bash or similar:

cd libexif-0.6.21
./configure --host=arm-linux-gnueabi CC=arm-linux-gnueabi-gcc AR=arm-linux-gnueabi-ar \ 
             --prefix=/home/me/build_exif/_install
make
make install

This should put the output files into /home/me/build_exif/_install ready for copying onto your ARM device.

Code to test u-blox Binary GPS Packet Checksum

Here’s something random for a Thursday, the following is some simple C++ code for checking the checksum of a u-blox binary GPS packet, for some reason we get quite a few packet data errors, so it turns out that it is important to check the checksum!

There must be an unwritten (or written?) software engineering rule which states that you should always check a checksum if one’s provided??!?

Note: Make sure to pass only complete packets to this function, it assumes it has everything to work with!

//
//
bool is_checksum_ok(unsigned char* buf) {
	// Packet format:
	// SYNC<1> SYNC<1> CLASS<1> ID<1> LENGTH<2> PAYLOAD CHKA<1> CHKB<1>
	// --------------- 4 ----------->|
	// --------------- 6 --------------------->|
	// --------------- 6 + length ---------------------------->|
	// Payload length field is little-endian (bless)
	unsigned short length = buf[5] << 8 | buf[4];
	unsigned char a = 0;
	unsigned char b = 0;
	// We calculate the checksum over the entire packet _except_
	// for the 2 bytes at the beginning and the 2 checksum bytes at
	// the end.
	for (unsigned short i = 2; i < 6 + length; ++i) {
		a += buf[i];
		b += a;
	}
	// Pull out checksum bytes
	unsigned char ra = buf[6 + length];
	unsigned char rb = buf[6 + 1 + length];
	// and compare
	return a = ra && b == rb;
}
//
//

IEEE has C as top Programming Language in 2016

The IEEE reports here that C is the number 1 programming language in 2016. As usual, I am slightly dubious about a lot of these rankings, each one seems to yield a different order. Still you would hope that the IEEE would employ a little more rigour than most! Funny thing is I just don’t see many of these C programmers about… Mind you I know one C programmer who personally outputs about 10 times the amount of code as most…

Friday – Programming in pure unadulterated C Today

Today I have the pleasure in programming in pure & unadulterated C, there is something really refreshing about the clean and minimal elegance of the language and its associated design principles, especially considering the weight of baggage the software engineering discipline has gathered of late – not bad for a rainy Friday!

2*c || !(2*c)

Image Storage and Indexing for Machine Vision Images

Every Software Engineer needs a hobby – to this end I have been toying with an idea for the last while.

There are many machine vision and computer vision applications that capture images from cameras and store them on disk. These applications can generate so many images that working with them can be quite difficult. For example consider an application that acquires from two cameras each acquiring at 30 frames per second – this application will save 216K images per hour, a 5 hour run would generate 1 million images!

Very often the images will be stored on a file system (local or networked) in some sort of hierarchical directory structure. Using a file system is a very efficient way of storing images, database systems (Relational or NoSQL) don’t offer many advantages and indeed can have associated disadvantages.

But how can we effectively work with so many images, we have possibly millions of images sitting in a set of directories, how can we interact with them and efficiently and query them based on attributes that are interest to us so the we can perform more analysis?

For example consider this set of (contrived) image queries:

Give me all of the images:

+ from camera 1
+ from camera 1 acquired on Sunday between 13:00 and 13:10
+ whose file size > 1MB
+ acquired within 100 meters of this GPS location
+ that have an average brightness > 63 Grey levels

Some people have attacked this image query problem by using a relational database to store image meta-data, if designed well this can allow for efficient image retrieval, however it seems to me that a schema-less approach is a better fit for images with dynamic attributes and I like the idea of not being tied down to any particular database technology and all of the baggage that comes with it.

So my idea is to start out on the road of implementing (for fun) a simple image indexing system for rather large sets of images, it will have an associated tool set, API and maybe even a query language in the future.

The system will:

Allow indexing of large numbers of images in arbitrary hierarchical directory structures

Index images based on standard attributes such as:

+ Acquisition Date/Time
+ Name
+ Source (e.g. camera)
+ Type
+ Size
+ Bit Depth
+ Exif Data, e.g.:
–> Location
–> Author
–> Acquisition parameters (aperture, exposure time etc.)
+ Etc.

Index images optionally based on Computer Vision metrics, e.g.
+ Brightness
+ Sharpness
+ Etc.

Allow users to define their own attributes for indexing, e.g.:
+ Define image attributes based on an OpenCV algorithm
+ Define attributes based on the contents of the image fie name.

The system will:

+ Have no dependencies on technologies such as Database systems etc.
+ Be cross platform

To get the ball rolling and so that we can say the first sod has been turned, here is some (naive) python which scans a directory tree of images and creates a flat CSV file of the image name, path and size:

#!/usr/bin/python
import argparse
import fnmatch
import os
import time
parser = argparse.ArgumentParser()
parser.add_argument("-p", "--path", help="The root path to the images directory tree")
args = parser.parse_args()
path = args.path
print 'looking in ' + path
ii = 0
start = time.time()
with open('%s/.flat' % path, 'w') as out:
    for root, _, filenames in os.walk(path):
        for name in fnmatch.filter(filenames, '*.jpg'):
            p = os.path.relpath(root, path)
            (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(os.path.join(root, name))
            f = {'name': name, 'path': p, 'size': size}
            out.write("i,%s,%s,%d\n" % (name, p, size))
            ii += 1
            if ii % 1000 == 0:
                print "Reading %d" % ii

duration = time.time() - start
print '%d images indexed in %d seconds, %d images/s' % (ii, duration, ii / duration)

Run it like this:

scanner.py --path "images\Run1\ccm17"

Once the directory tree has been walked and the CSV file generated we can use the following script to query images:

#!/usr/bin/python
import argparse
import os
import csv
parser = argparse.ArgumentParser()
parser.add_argument("-p", "--path", help="The root path to the images directory tree")
parser.add_argument("-w", "--where", help="The where value")
args = parser.parse_args()
path = args.path
print 'looking in ' + path
code = compile(args.where, '', 'eval')
images = []
index = (os.path.join(path, '.flat'))
jindex = (os.path.join(path, '.flat.json'))
print('opening index' + index)
class Image:
    def __init__(self, name, size, path):
        self.name = name
        self.size = size
        self.path = path
with open(index) as csvfile:
     spamreader = csv.reader(csvfile, delimiter=',', quotechar='|')
     for row in spamreader:
         images.append(Image(row[1], row[3], row[2]))
print 'index loaded'
for image in images:
    if eval(code):
        print('%s %s' % (image.name, image.size))

This allows us to run queries like this:

select.py --path "images\Run1\ccm17" --where "'_43' in image.name and image.size > 76000"

This will quickly list the images whose file size > 76000 bytes and whose name contains ‘_43’

This is a really simple first step but it does demonstrate how even a flat ‘index’ of attributes can be of great use.

Next Step:

+ Add more image attributes to the CSV file

Python script to query NTP status and return it as JSON

Setting up and automatically keeping an eagle eye on NTP from software can be a bit of a (difficult) black art. A common way to query NTP status is to run:

ntpq -p

This returns some NTP status information. It’s output can be a bit difficult to work with from your software, so I have included a small python 3 script which runs runs “ntpq -p” parses, and prints the output in Json format. So instead of shelling out to ntpq you can shell out to this script and deal with the resulting json string instead… This can be handy if you don’t have access to a Python NTP library.

#!/usr/bin/python
#   Copyright 2018 Kevin Godden
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
import subprocess
import json
import re
import sys
# Shell out to 'ntpq -p'
proc = subprocess.Popen(['ntpq', '-p'], stdout=subprocess.PIPE)
# Get the output
stdout_value = proc.communicate()[0].decode("utf-8")
#remove the header lines
start = stdout_value.find("===\n")
if start == -1:
    # We may be running on windows (\r\n), try \r...
    start = stdout_value.find("===\r")
    if start == -1:
        # No, go, exit with error
        result = {'query_result': 'failed', 'data': {}}
        print(json.dumps(result))
        sys.exit(1)
# Get the data part of the string
#pay_dirt = stdout_value[start+4:]
pay_dirt = stdout_value[start:]
# search for NTP line starting with * (primary server)
exp = ("\*((?P\S+)\s+)"
       "((?P\S+)\s+)"
       "((?P\S+)\s+)"
       "((?P\S+)\s+)"
       "((?P\S+)\s+)"
       "((?P\S+)\s+)"
       "((?P\S+)\s+)"
       "((?P\S+)\s+)"
       "((?P\S+)\s+)"
       "((?P\S+)\s+)")
regex = re.compile(exp, re.MULTILINE)
r = regex.search(pay_dirt)
# Did we get anything?
if not r:
    # No, try again without the * at the beginning, get
    # the first entry instead
    exp = (" ((?P\S+)\s+)"
           "((?P\S+)\s+)"
           "((?P\S+)\s+)"
           "((?P\S+)\s+)"
           "((?P\S+)\s+)"
           "((?P\S+)\s+)"
           "((?P\S+)\s+)"
           "((?P\S+)\s+)"
           "((?P\S+)\s+)"
           "((?P\S+)\s+)")
    regex = re.compile(exp, re.MULTILINE)
    r = regex.search(pay_dirt)
data = {}
if r:
    data = r.groupdict()
# Output Result
result = {'query_result': 'ok' if r else 'failed', 'data': data}
print(json.dumps(result))

GitHub repo is here.

RuneAudio working with HifiBerry Amp+ on RaspberryPi

Raspberrypi with hififiberry Amp+ and runeaudio hifi

This is a record of how I managed to the RuneAudio v0.3-beta software working with the excellent Hifiberry Amp+. The lads at runeaudio say that software release v0.4 will support Amp+ ‘out of the box’ but v0.3-beta doesn’t and they won’t/can’t say when v0.4 of the software will be released, however with some messing it can be made to work – which is great.

I really like runeaudio, and the sound quality with Hifiberry Amp+ and 2 venerable Monitor Audio speakers is really very good!

Anyway, all of the information required to setup runeaudio to work with the Amp+ can be found in bits and pieces on the runeaudio forum thatnks to everybody there! These steps worked for me during April 2015.

I found I had to add or modify a few steps so I will detail this here. These steps worked for me, but please backup before you proceed in case something goes wrong! To get going first install the runeaudio software for the raspberry pi.

The main set of steps to get Amp+ working can be found in this post by d2000:

http://www.runeaudio.com/forum/hifiberry-amp-t545.html

I just had to add a step or two as follows:

a.) Perform step 1.

b.) Execute:

pacman-db-upgrade

to update the database, this is a new step. Without this I got an error message when I tried to run step 2.

c.) Perform Steps 2 to 13

d.) Edit /boot/config.txt again and place the following line at the end of the file:

dtoverlay=hifiberry-amp

If you reboot at this stage you may find that browsing to runeaudio’s home page produces a 404 error (I did), we fix this in the next step as detailed in this post.

e.) Edit /usr/lib/systemd/system/redis.service and replace its contents with:

[Unit]
Description=Advanced key-value store
After=network.target
[Service]
#Type=forking
User=redis
PIDFile=/run/redis/redis.pid
ExecStartPre=/bin/mkdir -p /var/lib/redis
ExecStart=/usr/bin/redis-server /etc/redis.conf
ExecStop=/usr/bin/redis-cli shutdown
Restart=always
RestartSec=1
StartLimitInterval=30
StartLimitBurst=20
[Install]
WantedBy=multi-user.target

f.) Reboot

g.) Visit runeaudio, go to the Settings page and choose ‘HiFiBerry Amp’ in the ‘I²S kernel modules’ drop-down and click apply settings.

h.) Go to the runeaudio’s MPD page and choose ‘snd_rpi_hifiberry_amp’ in the ‘Audio output Interface’ drop-down.

i.) Check that ‘Volume Control’ setting is ‘Enabled – Software’

j.) Reboot

When the system restarts you should be able to play music with software volume control, you will find that hardware volume control won’t work with this set-up, the volume keeps popping back to 100 when you try to set it!

Hardware Volume Control

To get hardware volume control working I did the following, based on the advice in this post:

a.) Execute the following to list the configured sound devices:

aplay -l

The output will look something like this:

**** List of PLAYBACK Hardware Devices ****
card 0: sndrpihifiberry [snd_rpi_hifiberry_amp], device 0: HifiBerry AMP HiFi tas5713-hifi-0 []
  Subdevices: 1/1
  Subdevice #0: subdevice #0

Try to figure out which is yours, you can see from the output above that mine is device 0, sub-device 0.

b.) Edit /etc/mpd.conf and replace the ‘audio_output’ section at the end with the following, changing the values for ‘device’ and ‘mixer_device’ to match yours:

audio_output {
        name            "snd_rpi_hifiberry_amp"
        type            "alsa"
        device          "hw:0,0"
        mixer_type      "hardware"
        mixer_control   "Master"
        mixer_device    "hw:0"
        mixer_index     "0"
        auto_resample   "no"
        auto_format     "no"
        enabled         "yes"
}

d.) When runeaudio reboots it will revert the changes in mpd.conf so you have to prevent it by making the file read-only, do this at your own peril!

chattr +i /etc/mpd.conf

Then reboot, when the system restarts you should have hardware volume control and nicer sound, you will have to turn the volume up above 20 to hear it.

Links:
Thanks to all at the runeaudio forum!

http://www.runeaudio.com/forum/hifiberry-amp-t545.html
http://www.runeaudio.com/forum/getting-404-not-found-nginx-1-4-7-t909.html
http://www.runeaudio.com/forum/hifiberry-amp-volume-t1078.html

RaspberryPi media player rune aduio, hifiberry amp+

Data Security – How to safely ‘decommission’ and old hard drive and make an attractive ornament at the same time

software engineering ireland data security

Hint: I Pickaxe does the trick!