Showing posts with label diy. Show all posts
Showing posts with label diy. Show all posts

Fixing Raspberry Pi WiFi dropping issue

Many of our Raspberry Pi projects needs remote accessibility. This means you spends lot of time using either VNC or SSH to access the Raspberry Pi. Unfortunately, when wifi connectivity drops and failed to re-connect your whole world stops right? On such situations, physically accessing the RPi and doing the restart is really a pain.



Writing checkwifi.sh


You can just copy paste this script into "/usr/local/bin/checkwifi.sh"

ping -c4 192.168.1.1 > /dev/null
 
if [ $? != 0 ] 
then
  echo "No network connection, restarting wlan0...!"
  /sbin/ifdown 'wlan0'
  sleep 5
  /sbin/ifup --force 'wlan0'
fi

What we are trying to do is, we are restarting the wireless interface. First it tries to ping a given IP with `ping` command.

Then `$?` represents the exit code of the previous command. In our case, previous command is `ping`. If `ping` command fails will output a different exit code(not 0). Then the code part inside the `if` will be executed. It will turn off, wait 5 seconds and re-start the wireless interface `wlan0`.

And make sure you have necessary execute permissions for the script.

sudo chmod 775 /usr/local/bin/checkwifi.sh

Adding a Cron Job


Open up the cron tab editor by `crontab -e` and add the following lines to the bottom of the file.
*/5 * * * * /usr/bin/sudo -H /usr/local/bin/checkwifi.sh >> /dev/null 2>&1

This script will run every 5 minutes with sudo permission. And the script's output is redirected to /dev/null hence won't clog your sys logs.

Done

Everything is done! now restart your RPi and enjoy!

XMPP/Jabber for IoT Devices - Part 2

From the previous post we discussed nuts and bolts of the XMPP(or Jabber) and how we can make use of it in the context of Internet-of-Things(IoT). This blog post will demonstrate a sample implementation on XMPP using unofficial official IoT XEPs(XMPP Extensions). These XMPP extensions are still in "Experimental" state at the time of writing this blog post.

Today we will be using XEP0030(service discovery), XEP0323(IoT sensor data) and XEP325(IoT control) for this sample. We are using SleekXMPP python library as XMPP client which implements these XEPs.

STEP 1:
First we need to setup a XMPP server. If you haven't setup any XMPP server; please follow this blog post which will describe "How to Setting up XMPP Openfire Server and expose via RESTful APIs". Please note that you can use a XMPP Server from any XMPP vendor. Here we use Openfire since it shipped with Apache Open Source license. Once you have setup Openfire server you need to create two user accounts "bob" and "alice". Our user story is that "alice" has a sensor device called "firealarm" and "bob" needs to get sensor information (e.g. temperature) of the "firealarm":D. Please refer the following image.
How Bob can get sensor information from the Alice's Firealarm

STEP 2:
Copy paste below python scripts into two files "xmpp_server.py" and "xmpp_client.py". Or you can directly download both scripts from this zip archive.

STEP 3:
We need to install SleekXMPP library. Enter following to install SleekXMPP from PyPI (Please visit here to get more install options).
pip install sleekxmpp

STEP 4:
Once Openfire server is up and running you can issue following commands;
python xmpp_server.py -j "alice@127.0.0.1"  -p "password" -n "firealarm"
In another terminal;
python xmpp_client.py -j "bob@127.0.0.1"  -p "password" -c "alice@127.0.0.1/firealarm" -q


IoT Server Code(xmpp_server.py)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
import sys
from optparse import OptionParser
import socket

import sleekxmpp

# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
    from sleekxmpp.util.misc_ops import setdefaultencoding

    setdefaultencoding('utf8')
else:
    raw_input = input

from sleekxmpp.plugins.xep_0323.device import Device

class IoT_Server(sleekxmpp.ClientXMPP):
    """
    A simple IoT device that can act as server.

    This script can act as a "server" an IoT device that can provide sensor information.

    Setup the command line arguments.

    python xmpp_client.py -j "alice@yourdomain.com" -p "password" -n "device1" {--[debug|quiet]}
    python xmpp_client.py -j "alice@127.0.0.1" -p "password" -n "device1" {--[debug|quiet]}
    """
    def __init__(self, jid, password):
        sleekxmpp.ClientXMPP.__init__(self, jid, password)
        self.add_event_handler("session_start", self.session_start)
        self.add_event_handler("message", self.message)
        self.device = None
        self.releaseMe = False

    def testForRelease(self):
        # todo thread safe
        return self.releaseMe

    def doReleaseMe(self):
        # todo thread safe
        self.releaseMe = True

    def addDevice(self, device):
        self.device = device

    def session_start(self, event):
        self.send_presence()
        self.get_roster()
        # tell your preffered friend that you are alive
        # self.send_message(mto='jocke@jabber.sust.se', mbody=self.boundjid.bare +' is now online use xep_323 stanza to talk to me')

    def message(self, msg):
        if msg['type'] in ('chat', 'normal'):
            logging.debug("got normal chat message" + str(msg))
            ip = socket.gethostbyname(socket.gethostname())
            msg.reply("Hi I am " + self.boundjid.full + " and I am on IP " + ip + " use xep_323 stanza to talk to me").send()
        else:
            logging.debug("got unknown message type %s", str(msg['type']))

class IoT_Device(Device):
    """
    This is the actual device object that you will use to get information from your real hardware
    You will be called in the refresh method when someone is requesting information from you
    """

    def __init__(self, nodeId):
        Device.__init__(self, nodeId)
        logging.debug("=========TheDevice.__init__ called==========")
        self.temperature = 25  # define default temperature value
        self.update_sensor_data()

    def refresh(self, fields):
        """
        the implementation of the refresh method
        """
        logging.debug("=========TheDevice.refresh called===========")
        self.temperature += 1  # increment default temperature value by one
        self.update_sensor_data()

    def update_sensor_data(self):
        logging.debug("=========TheDevice.update_sensor_data called===========")
        self._add_field(name="Temperature", typename="numeric", unit="C")
        self._set_momentary_timestamp(self._get_timestamp())
        self._add_field_momentary_data("Temperature", self.get_temperature(),
                                       flags={"automaticReadout": "true"})

    def get_temperature(self):
        return str(self.temperature)

if __name__ == '__main__':
    #-------------------------------------------------------------------------------------------
    #   Parsing Arguments
    #-------------------------------------------------------------------------------------------
    optp = OptionParser()

    # Output verbosity options.
    optp.add_option('-q', '--quiet', help='set logging to ERROR',
                    action='store_const', dest='loglevel',
                    const=logging.ERROR, default=logging.INFO)
    optp.add_option('-d', '--debug', help='set logging to DEBUG',
                    action='store_const', dest='loglevel',
                    const=logging.DEBUG, default=logging.INFO)

    # JID and password options.
    optp.add_option("-j", "--jid", dest="jid",
                    help="JID to use")
    optp.add_option("-p", "--password", dest="password",
                    help="password to use")

    # IoT device id
    optp.add_option("-n", "--nodeid", dest="nodeid",
                    help="I am a device get ready to be called", default=None)

    opts, args = optp.parse_args()

    # Setup logging.
    logging.basicConfig(level=opts.loglevel,
                        format='%(levelname)-8s %(message)s')

    if opts.jid is None or opts.password is None or opts.nodeid is None:
        optp.print_help()
        exit()

    #-------------------------------------------------------------------------------------------
    #   Starting XMPP with XEP0030, XEP0323, XEP0325
    #-------------------------------------------------------------------------------------------

    # we bounded resource into node_id; because client can always call using static jid
    # opts.jid + "/" + opts.nodeid
    xmpp = IoT_Server(opts.jid + "/" + opts.nodeid, opts.password)
    xmpp.register_plugin('xep_0030')
    xmpp.register_plugin('xep_0323')
    xmpp.register_plugin('xep_0325')

    if opts.nodeid:
        myDevice = IoT_Device(opts.nodeid)
        xmpp['xep_0323'].register_node(nodeId=opts.nodeid, device=myDevice, commTimeout=10)
        while not (xmpp.testForRelease()):
            xmpp.connect()
            xmpp.process(block=True)
            logging.debug("lost connection")

IoT Client Code(xmpp_client.py)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
import sys
from optparse import OptionParser
import socket

import sleekxmpp
from sleekxmpp.exceptions import IqError, IqTimeout

# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
    from sleekxmpp.util.misc_ops import setdefaultencoding

    setdefaultencoding('utf8')
else:
    raw_input = input

PRINT_HEADER_LENGTH = 40

class IoT_Client(sleekxmpp.ClientXMPP):
    """
    A simple IoT device that can act as client

    This script can act as a "client" an IoT device or other party that would like to get data from
    another device.

    Setup the command line arguments.

    python xmpp_client.py -j "bob@yourdomain.com" -p "password" -c "alice@yourdomain.com/device1" {--[debug|quiet]}
    python xmpp_client.py -j "bob@127.0.0.1" -p "password" -c "alice@127.0.0.1/device1" {--[debug|quiet]}
    """
    def __init__(self, jid, password, sensorjid):
        sleekxmpp.ClientXMPP.__init__(self, jid, password)
        self.add_event_handler("session_start", self.session_start)
        self.add_event_handler("message", self.message)
        self.device = None
        self.releaseMe = False
        self.target_jid = sensorjid

    def datacallback(self, from_jid, result, nodeId=None, timestamp=None, fields=None,
                     error_msg=None):
        """
        This method will be called when you ask another IoT device for data with the xep_0323
        se script below for the registration of the callback
        """
        logging.debug("we got data %s from %s", str(result), from_jid)
        if (result == "fields"):
            header = 'XEP 302 Sensor Data'
            print('-' * PRINT_HEADER_LENGTH)
            gap = ' '* ((PRINT_HEADER_LENGTH - len(header)) / 2)
            print(gap + header)
            print('-' * PRINT_HEADER_LENGTH)

            logging.debug("RECV:"+str(fields))

            if len(fields) > 0:
                print "Name\t\tType\tValue\tUnit"
            for field in fields:
                print "  - " + field["name"] + "\t" + field["typename"] + "\t" + field["value"] + "\t" + field["unit"]
            print ""
            self.disconnect()

    def testForRelease(self):
        # todo thread safe
        return self.releaseMe

    def doReleaseMe(self):
        # todo thread safe
        self.releaseMe = True

    def addDevice(self, device):
        self.device = device

    def session_start(self, event):
        self.send_presence()
        self.get_roster()
        # tell your preffered friend that you are alive using generic xmpp chat protocol
        # self.send_message(mto='jocke@jabber.sust.se', mbody=self.boundjid.bare +' is now online use xep_323 stanza to talk to me')

        #-------------------------------------------------------------------------------------------
        #   Service Discovery
        #-------------------------------------------------------------------------------------------
        try:
            # By using block=True, the result stanza will be
            # returned. Execution will block until the reply is
            # received. Non-blocking options would be to listen
            # for the disco_info event, or passing a handler
            # function using the callback parameter.
            info = self['xep_0030'].get_info(jid=self.target_jid,
                                             node=None,
                                             block=True)
        except IqError as e:
            logging.error("Entity returned an error: %s" % e.iq['error']['condition'])
        except IqTimeout:
            logging.error("No response received.")
        else:
            header = 'XMPP Service Discovery'
            print('-' * PRINT_HEADER_LENGTH)
            gap = ' '* ((PRINT_HEADER_LENGTH - len(header)) / 2)
            print(gap + header)
            print('-' * PRINT_HEADER_LENGTH)

            print "Device: %s" % self.target_jid

            for feature in info['disco_info']['features']:
                print('  - %s' % feature)

        #-------------------------------------------------------------------------------------------
        #   Requesting data through XEP0323
        #-------------------------------------------------------------------------------------------
        session = self['xep_0323'].request_data(self.boundjid.full, self.target_jid,
                                                self.datacallback, flags={"momentary": "true"})

    def message(self, msg):
        if msg['type'] in ('chat', 'normal'):
            logging.debug("got normal chat message" + str(msg))
            ip = socket.gethostbyname(socket.gethostname())
            msg.reply("Hi I am " + self.boundjid.full + " and I am on IP " + ip + " use xep_323 stanza to talk to me").send()
        else:
            logging.debug("got unknown message type %s", str(msg['type']))

if __name__ == '__main__':
    #-------------------------------------------------------------------------------------------
    #   Parsing Arguments
    #-------------------------------------------------------------------------------------------
    optp = OptionParser()

    # JID and password options.
    optp.add_option("-j", "--jid", dest="jid",
                    help="JID to use")
    optp.add_option("-p", "--password", dest="password",
                    help="password to use")

    # IoT sensor jid
    optp.add_option("-c", "--sensorjid", dest="sensorjid",
                    help="Another device to call for data on", default=None)

    # Output verbosity options.
    optp.add_option('-q', '--quiet', help='set logging to ERROR',
                    action='store_const', dest='loglevel',
                    const=logging.ERROR, default=logging.INFO)
    optp.add_option('-d', '--debug', help='set logging to DEBUG',
                    action='store_const', dest='loglevel',
                    const=logging.DEBUG, default=logging.INFO)

    opts, args = optp.parse_args()

    # Setup logging.
    logging.basicConfig(level=opts.loglevel,
                        format='%(levelname)-8s %(message)s')

    if opts.jid is None or opts.password is None or opts.sensorjid is None:
        optp.print_help()
        exit()

    #-------------------------------------------------------------------------------------------
    #   Starting XMPP with XEP0030, XEP0323, XEP0325
    #-------------------------------------------------------------------------------------------
    xmpp = IoT_Client(opts.jid, opts.password, opts.sensorjid)
    xmpp.register_plugin('xep_0030')
    xmpp.register_plugin('xep_0323')
    xmpp.register_plugin('xep_0325')

    if opts.sensorjid:
        logging.debug("will try to call another device for data")
        xmpp.connect()
        xmpp.process(block=True)
        logging.debug("ready ending")

    else:
        print "noopp didn't happen"

XMPP/Jabber for IoT Devices - Part 1

Recently we implemented XMPP (or Jabber) support into our IoT Device Management Framework. Ideally this provides near realtime communication between multiple devices over multiple networks. The architecture of the Jabber server is conforms to an email server(server.com) where XMPP clients conforms email clients(user@server.com). Many chat clients implements XMPP to communicate between distributed networks near realtime.

Earlier we had the assumption of each device always have an accessible IP address. Based on this assumption we had an HTTP server running on the client-side. The APIs on the IoT Device Framework can call HTTP server on the device and perform actuators and sensor readings.

HTTP server based implementation
HTTP server based implementation


What's wrong with HTTP?
Let me explain why this doesn't work all time. Suppose you need to make your devices controllable through IoT Device Framework APIs over Internet. One option is to provide public IPs into each device. Oh God!, it is not a good solution.

Then other option is NAT. The devices are exposed into Internet over a NAT gateway. Outgoing messages from the device will be modified by NAT gateway so that they appear to be coming from the NAT gateway itself. The NAT gateway will record the changes it makes in its state table so that it can reverse the changes on return packets. NATs will keep the association for as long as the connection is alive. When your API calls returning into NAT gateway, Gateway itself might not be able to find return destination.

How NAT works
How NAT works (photo courtesy of How Stuff Works)
How XMPP handle it?
Let's see how XMPP can handle this. XMPP is the Internet Standard for real time messaging communication and presence. It uses a standard TCP connection. You can use fully-fledged light-weight XMPP pings (XEP-0199) to keep NAT records alive. Need to notice that XMPP can be also implemented over HTTP too(BOSH). Prime advantage of the XMPP is that you can address a device on "user@server.com/mobile" resource addressing scheme instead of IP of the device. XMPP can act

XMPP's basic model of communication is Client -> Server -> Server -> Client, and in support of this it defines a Client to Server protocol and a Server to Server protocol. Both of these protocols use XML encoded protocol directly over TCP. XMPP is a Bi-directional stream and "non-blocking":uses event-driven style. Powerful XMPP extensions such as Pub-Sub(XEP060) are available out-of-the box.
HTTP polling vs XMPP PubSub(source: [1])


How XMPP works?
The major steps for server-to-client communication as follows;
  1. Client and server communicate through port 5222 via a TCP connection.
  2. XMPP informations are contained on XML streams. A XMPP session is opened by <stream> tag and ended by </stream> tag.  All other parts are located between them.
  3. For security purpose, the open stream is followed by moderate Transport Layer Security (TLS)  negotiation and compulsory Simple Authentication and Security Layer (SASL) negotiation.
  4. A new stream will be opened immediately after the SASL negotiation, and it will be more secure and safe.
Please refer this post for more detail explanation.

XMPP Network Architecture
XMPP Network Architecture

The next blog-post will describe how you can configure XMPP client and a server based on unofficial official XMPP IoT devices standard.

Links:
[1] http://www.slideshare.net/igrigorik/event-driven-architecture-meshu-ilya-grigorik

Setting JAVA_HOME on Raspberry Pi

The new Raspbian build comes with Java 1.8 pre-installed. You can check the current java version by issuing following command;

java -version


java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) Client VM (build 25.0-b70, mixed mode)

In order to set environment variable "JAVA_HOME"; you can set it on ~/.bashrc file.

nano ~/.bashrc


then add the following into the end of file and save changes.

export JAVA_HOME="/usr/lib/jvm/jdk-8-oracle-arm-vfp-hflt"
export PATH=$PATH:$JAVA_HOME/bin

Then on you next login you can try out this;

export

which should ideally print;

declare -x JAVA_HOME="/usr/lib/jvm/jdk-8-oracle-arm-vfp-hflt"
declare -x PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games:/usr/lib/jvm/jdk-8-oracle-arm-vfp-hflt/bin"

Compiling ARM1176 for QEMU Raspberry Pi Emulation Kernel

This is referred from this forum post and optimized for Mac OSX users. This blog post will show you how to compile a QEMU-ready Linux kernel for the ARM1176JZF-S CPU used by the Raspberry Pi. If you want to skip all of this and just have a kernel you can use, you can download 3.18 kernel here.


1. Preparing the development environment

Installing brew

First you need to have a package manager. HomeBrew is a good choice(for more info. refer this post). Or you can use any other such as "macport".
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Installing libcurses5 library

libncurses5-dev
brew install homebrew/dupes/ncurses
brew link --force ncurses

Installing crosee-compiler tools

You can download pre-compiled cross-compiler tools for MAC OSX from this blog.
After the installation add the binaries to your path.
echo "/usr/local/linaro/arm-linux-gnueabihf-raspbian/bin" >> /etc/paths

2. Preparing the Linux Kernel

Installing Git

Installing any package on homebrew is simple as "brew install <package_name>".
brew install git

Download the source

Download linux source from raspberrypi linux repository.
git clone https://github.com/raspberrypi/linux.git

3. Configure the Kernel

Then we need to configure the kernel accordingly:
cd linux
make ARCH=arm versatile_defconfig
make ARCH=arm menuconfig

Specify the cross-compiler:

General Setup --->
    Cross-compiler tool prefix
        (arm-linux-gnueabihf-)

Note: Do not forget the '-' at the end of this string.
Note: When you select an option make sure you don't see 'M', but '*' (you may need to press space twice).

Select the right CPU options:

System Type --->
    [*] Support ARM V6 processor
    [*] ARM errata: Invalidation of the Instruction Cache operation can fail
    [*] ARM errata: Possible cache data corruption with hit-under-miss enabled

Enable support for hard-float binaries:

Floating point emulation  --->
    [*] VFP-format floating point maths

Enable ARM EABI:

Kernel Features --->
    [*] Use ARM EABI to compile the kernel
    [*] Allow old ABI binaries to run with this kernel

Enable QEMU's disk support:

Bus Support --->
    [*] PCI Support
Device Drivers --->
    SCSI Device Support --->
        [*] SCSI Device Support
        [*] SCSI Disk Support
        [*] SCSI CDROM support
        [*] SCSI low-lever drivers --->
        [*] SYM53C8XX  Version 2 SCSI support

Enable devtmpfs:

    Device Drivers --->
        Generic Driver Options--->
            [*] Maintain a devtmpfs filesystem to mount at /dev
            [*] Automount devtmpfs at /dev, after the kernel mounted the root

Enable the important file systems:

    File systems -->
        <*> Ext3 journalling file system support
         <*> The Extended 4 (ext4) filesystem

Enable tmpfs:

File systems --->
    Pseudo filesystems--->
        [*] Virtual memory file system support (former shm fs)

Enable the event interface:

Device Drivers --->
    Input device support--->
        [*] Event interface

Adding RaspberryPi Logo into Kernel:

Device Drivers --->
    Graphics Support --->
        Console display driver support --->
            [ ] Select compiled-in fonts
                [*] Bootup logo (optional)

Exit, saving the configuration.

4. Compiling the Kernel

make ARCH=arm
mkdir ../modules
make ARCH=arm INSTALL_MOD_PATH=../modules modules_install

Troubleshooting

Error 1:
scripts/mod/modpost.c:1465:7: error: use of undeclared identifier 'R_386_32'
        case R_386_32:

Create a new file elf.h and paste this code from the opensource.apple.com.

nano /usr/local/include/elf.h

In the same file append these...

#define R_386_NONE 0
#define R_386_32 1
#define R_386_PC32 2
#define R_ARM_NONE 0
#define R_ARM_PC24 1
#define R_ARM_ABS32 2
#define R_MIPS_NONE 0
#define R_MIPS_16 1
#define R_MIPS_32 2
#define R_MIPS_REL32 3
#define R_MIPS_26 4
#define R_MIPS_HI16 5
#define R_MIPS_LO16 6

Error2:

/usr/local/include/elf.h:45:10: fatal error: 'elftypes.h' file not found
#include "elftypes.h" /* In lieu of Solaris <sys/elftypes.h> */

create a new file elftypes.h and paste this code from  the opensource.apple.com.

nano /usr/local/include/elftypes.h

Error 3:

xargs: illegal option -- r

This is because BSD xargs doesn't have the same options as GNU xargs. Install GNU xargs from macports (It should show up earlier in your search path)

# Install GNU `find`, `locate`, `updatedb`, and `xargs`, g-prefixed
brew install findutils

Replace all files using 'xargs' to 'gxargs'. (Use a sublime or any texteditor).

5. Copy compiled Kernel

Copy the compiled kernel to your working directory:

cp arch/arm/boot/zImage ../

Done! You should name have a QEMU-bootable linux kernel and modules in your work directory.

Voice Recognition with Arduino

Recently I built a voice recognition circuit using Arduino. Voice recognition is not that easy with Arduino, It requires more processing and voice analyzing power. Keep in mind that all you have to deal with is a ATMega328P microcontroller running on 16MHz. That's why some modules like BitVoicer is outsourcing the processing power to the PC(Serial/UART or TCP/IP). But modules like uSpeech are completely independent of the PC and can run on its own. Thus, I used uSpeech.

uSpeech is a limited voice recognition software for the AVR. So, you can not directly write the text and expect it to recognize the speech. You need to deal with Phonemes. Hence,a fair amount of patient is needed. List of phonemes you would get is as below;

Phoneme      |   Literal       
--------------------------------------------------------------------
e            | The e sound.
--------------------------------------------------------------------   
h            | The `/sh/` sound. It can also be raised by 
             | `ch`, `j` and `z`
--------------------------------------------------------------------
v            | The `v` sound, occasionally triggered by  , 'z'
             | (may need to be fixed)
--------------------------------------------------------------------
f            | The `f` sound. 
--------------------------------------------------------------------
s            | The 's' sound.
--------------------------------------------------------------------
o            | 'a','o','i','r','l','m','n' and 'u' sounds. 
--------------------------------------------------------------------
' '          | Too Quiet for anything
--------------------------------------------------------------------

In order to get voice input you need a condenser mic with an operational amplifier (op-amp). The easiest way is to buy a MIC module which all are integrated. Try an ebay search.

You can wire it as below. 220 ohm resistors are used.

To Zoom in: click on the image

 This module has LM393 Op-amp chip and Electret condenser microphone.
This is what i bought from the ebay. Just 1.88 USD.
In uSpeech examples, what they actually compared is a single phoneme.It recognizes 6 phonemes (f, e, o, v, s, h). So it is handy for simple commands (if they have one of those phonemes), but it isn’t generalized at all. What i did is, I used a char[32] `buffer` to hold phoneme patterns. Then receiving `buffer` is compared with a list of predefined phoneme patterns. For instance my predefined patterns for green,orange and white are accordingly; "vvvoeeeeeeeofff", "hhhhhvoovvvvf", "hooooooffffffff". To compare which is the closest i used, minimum edit distance(Levenshtein distance). But how to ignore irrelevant patterns? you can use a threshold.

#include <uspeech.h>
#define ledGreen 7
#define ledOrange 6
#define ledWhite 5
#define MIN3(a, b, c) ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c)))
signal voice(A0);
const int BUFFER_MAX_PHONEMES=32;
char inputString[BUFFER_MAX_PHONEMES]; // Allocate some space for the string
byte index = 0; // Index into array; where to store the character
//
const int DICT_MAX_ELEMNTS=3;
char dict[DICT_MAX_ELEMNTS][BUFFER_MAX_PHONEMES]={"vvvoeeeeeeeofff","hhhhhvoovvvvf","hooooooffffffff"};
int LOWEST_COST_MAX_THREASHOLD=20;
void setup(){
  /*
  Phoneme      |   Literal       
  --------------------------------------------------------------------
  e            | The e sound.
  --------------------------------------------------------------------   
  h            | The `/sh/` sound. It can also be raised by 
               | `ch`, `j` and `z`
  --------------------------------------------------------------------
  v            | The `v` sound, occasionally triggered by  , 'z'
               | (may need to be fixed)
  --------------------------------------------------------------------
  f            | The `f` sound. 
  --------------------------------------------------------------------
  s            | The 's' sound.
  --------------------------------------------------------------------
  o            | 'a','o','i','r','l','m','n' and 'u' sounds. 
  --------------------------------------------------------------------
  ' '          | Too Quiet for anything
  --------------------------------------------------------------------
  */
  voice.f_enabled = true;
  voice.minVolume = 1500;
  voice.fconstant = 500;
  voice.econstant = 2;
  voice.aconstant = 4;
  voice.vconstant = 6;
  voice.shconstant = 10;
  voice.calibrate();
  Serial.begin(9600);
  pinMode(ledGreen, OUTPUT); 
  pinMode(ledOrange, OUTPUT); 
  pinMode(ledWhite, OUTPUT);
}

void loop(){
    voice.sample();
    char p = voice.getPhoneme();
    if(p==' ' || index >= BUFFER_MAX_PHONEMES){
      if(strLength(inputString)>0){
         Serial.println("received:"+String(inputString));
         parseCommand(inputString);
         inputString[0]=0;//clear string char array
         index=0;
      }
    }else{
      //printArray(voice.arr);
      inputString[index] = p; // Store it
      index++;
      inputString[index] = '\0'; // Null terminate the string
    }
}

char* guessWord(char* target){
  int len = strlen(target);//held target length
  
  for(int i=0;i<DICT_MAX_ELEMNTS;i++){
    //simple validation
    if(dict[i]==target){
      return dict[i];
    }
  }
  
  unsigned int cost[DICT_MAX_ELEMNTS];//held minimum distance cost

  //calculating each words cost and hits
  for(int j=0;j<DICT_MAX_ELEMNTS;j++){//loop through the dictionary
      cost[j]=levenshtein(dict[j],target);
      Serial.println("dict[j]="+String(dict[j])+" target="+String(target)+" cost="+String(cost[j]));
  }    
  
  //Determining lowest cost but still all letters in the pattern hitting word
  int lowestCostIndex=-1;
  int lowestCost=LOWEST_COST_MAX_THREASHOLD;
  for(int j=0;j<DICT_MAX_ELEMNTS;j++){//loop through the dictionary
    //Serial.println("dict[j]="+dict[j]+" dict[j].length()="+String(strlen(dict[j]))+" cost="+String(cost[j])+" hits="+String(hits[j])+" j="+String(j));
    if(cost[j]<lowestCost){
      lowestCost = cost[j];
      lowestCostIndex=j;
    }
  }
  
  //Serial.println("lowestCostIndex="+String(lowestCostIndex)+" lowestCost="+String(lowestCost));
  
  if(lowestCostIndex>-1){
    //Serial.println("lowestCost="+String(lowestCost)+" lowestCostIndex="+String(lowestCostIndex));
    return dict[lowestCostIndex];
  }else{
    return "";
  }
}

void parseCommand(char* str){
  char *gWord = guessWord(str);
  Serial.println("guessed :"+String(gWord));
  if(gWord==""){
    return;
  }else if(gWord==dict[0]){
    digitalWrite(ledGreen, HIGH);
    digitalWrite(ledOrange, LOW);
    digitalWrite(ledWhite, LOW);
  }else if(gWord==dict[1]){
    digitalWrite(ledGreen, LOW);
    digitalWrite(ledOrange, HIGH);
    digitalWrite(ledWhite, LOW);
  }else if(gWord==dict[2]){
    digitalWrite(ledGreen, LOW);
    digitalWrite(ledOrange, LOW);
    digitalWrite(ledWhite, HIGH);
  }
}

unsigned int levenshtein(char *s1, char *s2) {
    unsigned int s1len, s2len, x, y, lastdiag, olddiag;
    s1len = strlen(s1);
    s2len = strlen(s2);
    unsigned int column[s1len+1];
    for (y = 1; y <= s1len; y++)
        column[y] = y;
    for (x = 1; x <= s2len; x++) {
        column[0] = x;
        for (y = 1, lastdiag = x-1; y <= s1len; y++) {
            olddiag = column[y];
            column[y] = MIN3(column[y] + 1, column[y-1] + 1, lastdiag + (s1[y-1] == s2[x-1] ? 0 : 1));
            lastdiag = olddiag;
        }
    }
    return(column[s1len]);
}

int strLength(char const* s) {
    int i = 0;
    while (s[i] != '\0' && s[i] != ' ')
        ++i;

    return i;
}

How to install uSpeech? download the zip file from here. Then, extract the files in to your arduino `Library` folder. In my case, it is on 'C:\Program Files (x86)\Arduino\libraries'. It should be in a separate folder, let's say 'uSpeech'. Finally, restart the Arduino IDE.


Coding more than Four `7segment` display panels on Arduino

Recently I coded a seven segment display for a friend of mine. Hope this might be helpful for wiring purposes. My task was to correctly display 5 digit number on 5 `seven segments`. So, most of the examples out there including this module support only up to 4 segments. So, I wrote my own code from the scratch.


This is the code, I wrote:

const int SEGMENT_RENDER_DELAY_MS=5;//segment rendering delay
const int MAX_NUM_SEGMENTS=5;//store maximum segments
//
String inputString = ""; // a string to hold incoming data
String displayStr="hellO"; //current display string (keep empty to display all zero's!)
//unsigned long digit; // display digit, if you are dealing only with numbers
//           a
//        =======
//      ||       ||
//    f ||       || b
//      ||   g   ||
//        =======
//      ||       ||
//    e ||       || c
//      ||   d   ||
//        =======
 
byte seven_seg[16][9] = { {0,0, 1,1,1,1,1,1,0 },  // = 0
                         {0,0, 0,1,1,0,0,0,0 },  // = 1
                         {0,0, 1,1,0,1,1,0,1 },  // = 2
                         {0,0, 1,1,1,1,0,0,1 },  // = 3
                         {0,0, 0,1,1,0,0,1,1 },  // = 4
                         {0,0, 1,0,1,1,0,1,1 },  // = 5
                         {0,0, 1,0,1,1,1,1,1 },  // = 6
                         {0,0, 1,1,1,0,0,0,0 },  // = 7
                         {0,0, 1,1,1,1,1,1,1 },  // = 8
                         {0,0, 1,1,1,0,0,1,1 },   // = 9
                         {0,0, 1,0,0,1,1,1,1 },   // = e
                         {0,0, 0,0,0,0,1,0,1 },   // = r
                         {0,0, 0,0,1,1,1,0,1 },   // = o
                         {0,0, 1,1,1,1,1,1,0 },   // = O
                         {0,0, 0,1,1,0,1,1,1 },   // = h
                         {0,0, 0,0,0,1,1,1,0 }   // = l
                         };
                         
byte firstSegmentPos = 0, secSegmentPos = 0,thirdSegmentPos = 0, fourthSegmentPos = 0, fifthSegmentPos = 0;
//
int pin;

void setup() {    
   // initialize serial:
  Serial.begin(9600);
  // reserve 200 bytes for the inputString:
  inputString.reserve(200);
  
  //define output pins
  pinMode(2, OUTPUT);   
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);
}

void loop() {
   updateSegments();
}

/*
  SerialEvent occurs whenever a new data comes in the
 hardware serial RX.  This routine is run between each
 time loop() runs, so using delay inside loop can delay
 response.  Multiple bytes of data may be available.
 */
void serialEvent() {
 
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read(); 

    //if linebreak found, parse it
    if (inChar == '\r'||inChar == '\n') {
      //we got a break, parse string
      parseCode(&inputString);
      inputString="";
    }else{
      //append the character received
      inputString += inChar;
    }

  }
}

/**
* Parses one line of transaction
* If validation failed-> error displayed Or value displayed
**/
void parseCode(String* receivedLine){
  String line = *receivedLine;
  line.trim();//remove spaces
  //
  int lineLength=line.length();//length of the line received

  String rv="";
  if(lineLength <= MAX_NUM_SEGMENTS){
    rv=line;//set to display value
    //nSuccessTrans++;
  }else{
    rv="error";
    //nFailTrans++;
  }
  displayStr=rv;//set to display value
  
  //digit = displayStr.toInt();// if you are dealing only with numbers
}

/**
* Updates Display Segments
**/
void updateSegments(){
  /*
  fifthSegmentPos = digit / 10000;
  digit %= 10000;
  fourthSegmentPos = digit / 1000;
  digit %= 1000;
  thirdSegmentPos = digit / 100;
  digit %= 100;
  secSegmentPos = digit / 10;
  firstSegmentPos = digit % 10;
  */
  firstSegmentPos = GetSegmentCharPos(&displayStr,1); 
  secSegmentPos = GetSegmentCharPos(&displayStr,2); 
  thirdSegmentPos = GetSegmentCharPos(&displayStr,3); 
  fourthSegmentPos = GetSegmentCharPos(&displayStr,4); 
  fifthSegmentPos =  GetSegmentCharPos(&displayStr,5);

  //Serial.println("first segment");
  digitalWrite(9,HIGH);   
  digitalWrite(10,LOW); 
  digitalWrite(11,LOW);   
  digitalWrite(12,LOW);   
  digitalWrite(13,LOW);     
  displaySegmentChar(&firstSegmentPos);
  delay(SEGMENT_RENDER_DELAY_MS);
  
  //Serial.println("second segment");
  digitalWrite(9,LOW);   
  digitalWrite(10,HIGH);
  digitalWrite(11,LOW);   
  digitalWrite(12,LOW);   
  digitalWrite(13,LOW);   
  displaySegmentChar(&secSegmentPos);
  delay(SEGMENT_RENDER_DELAY_MS);
  
  //Serial.println("third segment");
  digitalWrite(9,LOW);   
  digitalWrite(10,LOW);
  digitalWrite(11,HIGH);   
  digitalWrite(12,LOW);   
  digitalWrite(13,LOW);   
  displaySegmentChar(&thirdSegmentPos);
  delay(SEGMENT_RENDER_DELAY_MS);
  
  //Serial.println("fourth segment");
  digitalWrite(9,LOW);   
  digitalWrite(10,LOW);
  digitalWrite(11,LOW);   
  digitalWrite(12,HIGH);   
  digitalWrite(13,LOW);   
  displaySegmentChar(&fourthSegmentPos);
  delay(SEGMENT_RENDER_DELAY_MS);
  
  //Serial.println("fifth segment");
  digitalWrite(9,LOW);   
  digitalWrite(10,LOW);
  digitalWrite(11,LOW);   
  digitalWrite(12,LOW);   
  digitalWrite(13,HIGH);   
  displaySegmentChar(&fifthSegmentPos);
  delay(SEGMENT_RENDER_DELAY_MS);
}

/**
* Returns position of the rendering `segment character`
**/
static int GetSegmentCharPos(String* strNum, int pos){
        String strNumber = *strNum;
        if(strNumber.length()==0) return 0;
        if(strNumber.length()>=pos){
           char c = strNumber.charAt(strNumber.length()-pos);
           if(c=='e') return 10;//e
           if(c=='r') return 11;//r
           if(c=='o') return 12;//o
           if(c=='O') return 13;//O
           if(c=='h') return 14;//h
           if(c=='l') return 15;//l
           if(c-'0'>0) return c-'0';//digit [0-9]
        }
        return 0;
}
/**
* Sketches single digit
**/
void displaySegmentChar(byte* pos)  {
   int digitPos = *pos;
   for(pin = 2;pin < 9; pin++) {
        digitalWrite(pin,seven_seg[digitPos][pin]);
   }
}
 This code holds to be displayed code as a String object i.e. `displayStr` because i needed to display non-numeric characters as well(e.g. `error`). If you only need digits, you may use unsigned long called `digit`. And you can use following code to get segment numbers;
  fifthSegmentPos = digit / 10000;
  digit %= 10000;
  fourthSegmentPos = digit / 1000;
  digit %= 1000;
  thirdSegmentPos = digit / 100;
  digit %= 100;
  secSegmentPos = digit / 10;
  firstSegmentPos = digit % 10;

ATMega328P Malfunctioning

ATMega328P Malfunctioning
As i mentioned in my previous post, I ordered a chinese Arduino UNO R3 version board. It is pretty cool compared to genuine more additional features. But you'd not get default LED light connected to the pin 13 and Tx Rx LEDs.

This is my experience with Arduino UNO R3 version board. Since I already worked with Arduino boards at Uni, I was little bit surprised since once you connected USB cable to the PC and If you correctly installed Arduino IDE(with drivers) but STILL it is not detecting my board.

After spending couple of times, I realized that these version boards using a different chip for USB-to-Serial communication namely CH340/CH341 instead of ATmega16U2. Finally i installed CH340 Serial drivers and it connected perfectly.

Then i got this "Blink" example from the IDE and tried to upload it.But unfortunately It gave following error.



This is because Arduino IDE tells ATMEGA328P's bootloader to write some bytes, then IDE tries to read again from the bootloader to verify. But it fails. which means bootloader unable to write to its flash. So i tried several times several options which is on the Internet; but never worked out. 

Finally i concluded that my ATMEGA328P is no longer usable.

Arduino Introduction

I recently started developing with Arduino. In the old days we had to develop everything from the scratch. Arduino and other DIY kits makes it lot easier. If you are in two minds whether to buy Arduino or `Raspberry Pi`. I would simply say, If your project is something to do with hardware Arduino is most suitable. If your project is something to do with software more then `Raspberry Pi` would be a better choice. But notice that you need to think other factors as well.

I ordered chineese Ardunio UNO R3 version board. Almost every functionality as original UNO and few things more. major difference is CH340 chip for USB-Serial instead of ATmega16U2.So it won't be automatically detect and install drivers for you. You need to install CH340 drivers first. Main programmable chip is same ATMega328P.

this is how Arduino UNO clone look likes
But never trust, a chinese... received a malfunctioning ATMEGA328P microcontroller. So I went pettah market(Sri Lanka), ATMega328P microcontroller costs 390 LKR only. Finally seller agreed to refund 5 USD.

However i bought another Arduino UNO from here SL, costed me around 1600 LKR. Atleast it is more look like genuine.

this is another Arduino UNO more or less look like genuine
Following are the specifications of Arduino UNO R3;


MicrocontrollerATMega328P 
Clock Speed16MHz (but ATMega328P supports 20MHz)
Flash Memory32K (ATMega328P) 0.5KB used by bootloader
EEPROM1K (ATMega328P)
SRAM2K (ATMega328P)
Digital I/O Pins14(in which 6 PWM output supported)
Analog Output Pins6
DC current per I/O pin40mA
DC current per 3.3V pin50mA
Operating Voltage5V
Input Voltage6-20V but 7-12V recommended

Arduino UNO R3 Pinout diagram