Showing posts with label Bluetooth. Show all posts
Showing posts with label Bluetooth. Show all posts

Monday, 2 July 2018

Give your old audio amplifiers a new lease of life with this RM11.90 bluetooth audio receiver


Chinese no-name Bluetooth Audio Receiver
Over the years we kept upgrading the living room audio systems: first turntable gave way to the cassette tape deck, which then changed to the CD player, then mp3 player. This resulted in a few orphaned amplifiers and speakers, especially if they have odd impedances like 4 or 6 Ohms.

These days we play most of our music from smartphones; why not convert them to bluetooth speakers? I used this RM11.90 sgrobot bluetooth audio receiver.

Note the LED, resistor and capacitor
It came with some loose parts: an LED, 100 Ohm resistor and an electrolytic capacitor. Somehow it reminded me of excess bluetooth audio receiver modules off a PCB mainboard for a bluetooth speaker or somesuch now sold off cheaply. The IC markings C7THN5004 did not come up on an Internet search; they look like custom markings. Never mind, better this than discarding it in the municipal landfill.

gameinstance.com has a good write-up on it:




I soldered the parts on it; it literally took one minute.


The pinouts are printed on the PCB, but here they are anyway:



3rd pin from top: left speaker, 4th pin: right speaker
I powered it from my trusty D-link USB3 hub, which puts out a whopping 2A at 5V.

To test, I connected it to yet another one of my orphaned PC analog speakers with integrated audio amplifier. I used my Raspberry Pi Zero W with a brand-new version of Raspbian (Debian 'stretch') and omxplayer. It worked without fuss.  It came up as WIN-668 and paired without asking for a PIN.

I had less luck with my Raspberry Pi B+. It had built-in analog and HDMI audio and omxplayer just would not work out of the box. I'm sure that is fixable, but that is another blog post.

As usual, if you can't be bothered with this DIY malarkey, you can buy it ready-made for RM16.90:
RM29: Bluetooth audio receiver. The USB connector is for power only
Once the speakers are bluetooth-enabled, especially combined with the Raspberry Pi Zero W, it becomes and Internet of Things (IoT) device. Now applications like your very own DIY Google Home becomes possible.

Not bad for something one step from the rubbish heap. Happy Trails.

Tuesday, 12 June 2018

Raspberry Pi Zero W as IoT Bluetooth Gateway Part3: Replacing Slackware with Raspbian

Something's missing ... yes the USB bluetooth dongle

Nowadays I use the iterative development method. Following Part 1 and Part 2, we used the Raspberry Pi Zero W as an IoT Bluetooth Gateway daily for about six months. It was more secure than using the smartphone to directly pair to the autogate bluetooth module HC-06 - the PIN is only 4 digits and easily brute-forced.. Using a WiFi repeater, its range was much better. We could now reliably operate the autogate without getting out of the car.

In addition the Pi Zero W gateway hogged the HC-06 all the time, and prevented any intruder from pairing to it. To gain access the intruder would now have to brute-force the WPA2 passphrase in the WiFi repeater, a much tougher opponent.

It also got turned on and off a lot to avoid being damaged by lightning, and sometimes it would fail to start up properly. This is usually due to HC-06 connectivity issues- perhaps it was not unpaired properly or was later that usual being discovered. This allowed the startup to be adjusted using my bash script in /etc/rc.d/rc.local.

Now that it was stable enough it was time to improve it further. From Part 2, Slackware 14.2 did not recognize the Raspberry Pi Zero W's builtin bluetooth module. Perhaps the Raspbian kernel I used was not current enough or perhaps my Slackware installation lacked something, but having to use an RM32 bluetooth dongle with an RM42.20 Raspberry Pi Zero W rankled.

I downloaded the Raspberry Pi NOOBS, and following the installation guide, installed the latest Raspbian OS into by Pi Zero W. Make sure to set up the WiFi connection to your broadband. The builtin bluetooth controller worked first time and had no trouble working with the HC-06 in the autogate. I repeated the procedure in Part 1, and upgraded the firmware/BIOS in the Slackware 14.1 image, but the builtin bluetooth controller did not come up.

Now I could have gone further and upgraded the Linux kernel as well, but perhaps it is time to work with Raspbian/Debian for a while. At least I would not have to keep up with the updates.

To use Raspbian for the IoT Bluetooth Gateway, I needed pymodbus, apache, and php. In Slackware, pymodbus needed upgrades to python 2.7, pip and pysetuptools. Checked in Raspbian:

root@raspi-0-w-2:/root# pip --version
pip 9.0.1 from /usr/lib/python2.7/dist-packages (python 2.7)
root@raspi-0-w-2:/root# python --version
Python 2.7.13

Now that was a pleasant surprise: no upgrades necessary. Now for pysetuptools: Make sure your WiFi connection is working before you try this:

root@raspi-0-w-2:/root# apt-get upgrade python-setuptools
Reading package lists... Done
Building dependency tree
Reading state information... Done
python-setuptools is already the newest version (33.1.1-1).
python-setuptools set to manually installed.
Calculating upgrade... Done
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

Again, no upgrades necessary. Might as well go ahead with pymodbus:

root@raspi-0-w-2:/root# pip install -U pymodbus

Installed successfully, no fuss. Now my Modbus server program needed twisted, so:

root@raspi-0-w-2:/root# pip install twisted

No problems there. Also required is cryptography:

root@raspi-0-w-2:/root# pip install cryptography

Now for apache. Before that just to be sure I checked for upgrades to Raspbian:

root@raspi-0-w-2:/root# apt-get update

No upgrades necessary- I am beginning to like Debian. Now for apache:

root@raspi-0-w-2:/root# apt-get install apache2 -y

No fuss, apache ran right out of the box (check using http://localhost/index.html):

root@raspi-0-w-2:/root# find /var -name index.html
/var/www/html/index.html

root@raspi-0-w-2:/root# ps -ef
root     20567     2  0 14:14 ?        00:00:00 [kworker/0:3]
root     21396     1  0 14:16 ?        00:00:00 /usr/sbin/apache2 -k start
www-data 21398 21396  0 14:16 ?        00:00:00 /usr/sbin/apache2 -k start
www-data 21399 21396  0 14:16 ?        00:00:00 /usr/sbin/apache2 -k start
root     21723  1705  0 14:18 pts/1    00:00:00 ps -ef

Similarly, php just worked, striaght from the box:

root@raspi-0-w-2:/root# apt-get install libapache2-mod-php php

The web server was linked to pymodbus using the same method as before. Now Raspbian defaults to using dynamic IP, and ssh disabled, which is not very useful if you are using the Pi in "headless" (ie without monitor, mouse or keyboard). ssh is easily enabled via the Raspbian desktop or using raspi-config. To get it to use static IP simply add the following lines to /etc/dhcpcd.conf:

interface wlan0
static ip_address=192.168.1.2/24
static routers=192.168.1.1
static domain_name_servers=8.8.8.8

Lastly Raspbian uses the much improved bluetoothctl, which is an interactive program and not easily included in a bash script like bluez. Luckily Linux has just the thing we need: 'expect' can be used to fool bluetoothctl into thinking it is interacting with a human via the keyboard.

root@raspi-0-w-2:/root# apt-get install expect

Put it in a little bash script and you have a much more robust startup script than simple-agent:

root@raspi-0-w-2:/root# cat ./hc-06.sh
#!/usr/bin/expect -f

set prompt "#"
set address [lindex $argv 0]

spawn sudo bluetoothctl -a
expect -re $prompt
send "remove $address\r"
sleep 1
expect -re $prompt
send "scan on\r"
send_user "\nSleeping\r"
sleep 5
send_user "\nDone waiting for controller\r"
expect "Controller"
send_user "\nSleeping ... waiting for autogate\r"
sleep 5
send_user "\nDone waiting for autogate\r"
expect "HC-06"
send "scan off\r"
send "trust $address\r"
sleep 2
send "pair $address\r"
sleep 2
send "0000\r"
sleep 3
send_user "\nShould be paired now.\r"
send "quit\r"
expect eof

Note: replace '0000' with your password and launch the script thus:

root@raspi-0-w-2:/root# ./hc-06.sh 12:34:56:78:9A:BC

Lastly bind to your bluetooth device to get the serial port /dev/rfcomm0:
rfcomm bind 0 12:34:56:78:9A:BC 1

And you are ready for the pymodbus program, which I launched in the background and captured its output to a log file:

root@raspi-0-w-2:/root# python ./autogate_server.py >> ./autogate.log 2>> ./autogate.log &

Raspbian turned out to be quite pleasant to use, but then it is Linux, so we expect nothing less. 

Happy Trails.

Sunday, 21 January 2018

Raspberry Pi Zero W as IoT Bluetooth Gateway Part2: Replacing the Laptop

Raspberry Pi Zero W as Bluetooth IoT Gateway installed in the front porch
The original Bluetooth Autogate Remote used a recycled Acer Aspire F15 laptop as an IoT gateway. The laptop ran an Apache webserver for the html/php program. This went very well until a fortnight ago when we had a cool rainy spell and the garden got waterlogged, which always seem to affect wireless communications at the Autogate control box located in the gatepost.

The Bluetooth Autogate Remote started to fail, first on individual button presses, then the laptop bluetooth controller lost the link to the HC-06 in the gatepost. A typical bluetoothctl command sequence and output now goes something like this:

[bluetooth]# scan on
Discovery started
[CHG] Controller C8:FF:28:27:7D:2C Discovering: yes
[CHG] Device 98:D3:32:20:BB:7B RSSI: -90
[bluetooth]# devices
Device 98:D3:32:20:BB:7B HC-06
[bluetooth]# pair 98:D3:32:20:BB:7B
Attempting to pair with 98:D3:32:20:BB:7B
Failed to pair: org.bluez.Error.ConnectionAttemptFailed

When RSSI value got to about -86 the pairing would work but dropped every few days or so. This handy link explained quite well that RSSI meant 'Received Signal Strength Indicator' and has an RSSI table:

Acceptable Signal Strengths

Signal StrengthTL;DRRequired for
-30 dBmAmazingMax achievable signal strength. The client can only be a few feet from the AP to achieve this. Not typical or desirable in the real world.N/A
-67 dBmVery GoodMinimum signal strength for applications that require very reliable, timely delivery of data packets.VoIP/VoWiFi, streaming video
-70 dBmOkayMinimum signal strength for reliable packet delivery.Email, web
-80 dBmNot GoodMinimum signal strength for basic connectivity. Packet delivery may be unreliable.N/A
-90 dBmUnusableApproaching or drowning in the noise floor. Any functionality is highly unlikely.N/A

So my RSSI went from 'Not Good' to 'Unusable'. Now the laptop's bluetooth controller had 2 walls and a plastic enclosure between it and the Autogate Bluetooth HC-06, so I though it might be a good idea to have the IoT Gateway on the front porch, which not only is only 30m away but had line-of-sight to the HC-06.

That was the purpose of the Raspberry Pi Zero W. My last post showed Slackware 14.1 can run on it. This is handy because my laptop ran Slackware 14.2.

First I need to get the WiFi running on the Raspberry Pi Zero W. That was easy enough. It turned out I was missing some bios files:
-rwxr-xr-x 1 root root     6551 Oct 30 18:36 fixup.dat
-rwxr-xr-x 1 root root     2578 Oct 30 18:36 fixup_cd.dat
-rwxr-xr-x 1 root root     9694 Oct 30 18:36 fixup_db.dat
-rwxr-xr-x 1 root root     9694 Oct 30 18:36 fixup_x.dat
-rwxr-xr-x 1 root root  2820196 Oct 30 18:36 start.elf
-rwxr-xr-x 1 root root   667460 Oct 30 18:36 start_cd.elf
-rwxr-xr-x 1 root root  4956676 Oct 30 18:36 start_db.elf
-rwxr-xr-x 1 root root  3904228 Oct 30 18:36 start_x.elf

These files were really a set (startup.elf needed fixup.dat and so on) and in my case I had start.elf but not fixup.dat. In addition I was missing the file:
-rwxr-xr-x 1 root root    15830 Oct 30 18:36 bcm2708-rpi-0-w.dtb

Once corrected the WiFi interface came up and was connected to my home WiFi hotspot with no fuss. I could now ssh into the Zero W and use it without the monitor or USB keyboard. 

The bluetooth controller was a whole different ballgame. After spending a whole Sunday on it, I gave up and decided to use a Vztech bluetooth dongle instead. This I connected to the micro USB OTG connector and worked straight out of the box.

The bluetooth software was the same Bluez used in Slackware 14.2, but being the 14.1 version did not have bluetoothctl. The function I missed most was the bluetooth agent required to pair the HC-06 to the Pi Zero W. Happily a little digging showed that Bluez tarball for Slackware 14.1 has a workable agent, simple-agent which was built but not included when the Slackware upgradepkg program installed Bluez.

I just needed to unpack the tarball and install it:

root@rpi-0-w:~# cp  /tmp/bluez-4.99/test/simple-agent /usr/bin/

Now without bluetoothctl the commands are a little different but still quite straightforward:

root@rpi-0-w:~# chmod +x /etc/rc.d/rc.bluetooth

Reboot, taking care to first plug in the bluetooth dongle, then do:
root@rpi-0-w:~# hcitool dev
Devices:
        hci0    00:1A:7D:DA:71:13

And more important:
root@rpi-0-w:~# hciconfig
hci0:   Type: BR/EDR  Bus: USB
        BD Address: 00:1A:7D:DA:71:13  ACL MTU: 310:10  SCO MTU: 64:8
        DOWN
        RX bytes:574 acl:0 sco:0 events:30 errors:0
        TX bytes:368 acl:0 sco:0 commands:30 errors:0

root@rpi-0-w:~# hciconfig hci0 up

And now the bluetooth controller is running:

root@rpi-0-w:~# hciconfig hci0
hci0:   Type: BR/EDR  Bus: USB
        BD Address: 00:1A:7D:DA:71:13  ACL MTU: 310:10  SCO MTU: 64:8
        UP RUNNING
        RX bytes:1148 acl:0 sco:0 events:60 errors:0
        TX bytes:736 acl:0 sco:0 commands:60 errors:0

Next you need to find the bluetooth channel:

root@rpi-0-w:~# sdptool browse local

Which produced output like this (look for RFCOMM):

Service Name: Dial-Up Networking
Service RecHandle: 0x10005
Service Class ID List:
  "Dialup Networking" (0x1103)
  "Generic Networking" (0x1201)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 1
Profile Descriptor List:
  "Dialup Networking" (0x1103)
    Version: 0x0100

Which means we are channel 1. We then look for the HC-06:

root@rpi-0-w:~# hcitool scan
Scanning ...
        98:D3:32:20:BB:7B       HC-06

Next we launch the bluetooth agent:

root@rpi-0-w:~simple-agent hci0 98:D3:32:20:BB:7B
RequestPinCode (/org/bluez/481/hci0/dev_98_D3_32_20_BB_7B)
Enter PIN Code: 1234
Release
New device (/org/bluez/481/hci0/dev_98_D3_32_20_BB_7B)

The last step is then:

root@rpi-0-w:~/bluez# rfcomm bind /dev/rfcomm0 98:D3:32:20:BB:7B 1

And the all-important RSSI is now in 'Amazing' category:

root@rpi-0-w:~# hcitool rssi 98:D3:32:20:BB:7B
RSSI return value: -19

The next steps involve setting up python, pymodbus and apache in Slackware 14.1, and are best treated separately. You can also refer to other online links for this. The end result is much the same as the previous post, and worked about the same. Not bad for the RM42 Raspberry Pi Zero W, which replaced an RM3,200 Acer AspireF15 as the IoT Bluetooth Gateway.

Now all this seemed a little fly-by-the-seat-of-your-pants, but this is the nature of DevOps/Rapid Development. It is iterative and relies on incremental redesigns with the emphasis on putting out the prototype as quickly as possible. Notice it took a few weeks operating the prototype for the bluetooth wireless problem to be obvious. 

Some problems are immediately obvious: the Pi Zero W does not have battery backup while the laptop did. Installing a battery would require other things: a proper enclosure, perhaps a shelf. But in the meantime up goes the latest prototype to fish for other problems. 

Perhaps one of the most important things about Rapid Development is customer involvement. One indication the project is going well is the wife got quite used to opening the autogate from her car, and complained immediately when the Bluetooth Autogate stopped working in wet weather.   

Happy Trails.

Update: as feared, every few days or so the Raspberry Pi Zero W rebooted by itself. This was tracked by having the pymodbus server program print out regular heatbeat messages:

 Cmd z, voltages are [3, 255, 2, 157]
stored values= [3, 252, 2, 154]
address= 0  answer:  [3, 255, 2, 157]      
CPU 1023 Battery 669
CPU 5.00 Battery 13.88 Volts 2018-01-26 04:34:13
Reply: ÿHCM¡LJP len 10                              

 Cmd z, voltages are [3, 255, 2, 161]
stored values= [3, 255, 2, 157]  
address= 0  answer:  [3, 255, 2, 161]
CPU 1023 Battery 673
CPU 5.00 Battery 13.97 Volts 2018-01-26 04:46:14                              
Reply: ïHCMLJP len 10    

 Cmd z, voltages are [3, 239, 2, 156]
stored values= [3, 255, 2, 161]
address= 0  answer:  [3, 239, 2, 156]
CPU 1007 Battery 668  
CPU 4.92 Battery 13.86 Volts 2018-01-26 04:58:15
packet_write_wait: Connection to 172.16.1.25 port 22: Broken pipe

We had gotten really used to that WiFi autogate remote, so to keep it going (while I am trying to figure out what went wrong) I simply added this to /etc/rc.d/rc.local so that the IoT gateway program restarted itself. Not using bluetoothctl had a silver lining after all:

echo "1234" | simple-agent hci0 98:D3:32:20:BB:7B release
rfcomm bind 0 98:D3:32:20:BB:7B 1
sleep 2
python ./autogate_server.py > /dev/null &

Friday, 19 January 2018

Raspberry Pi Zero W as IoT Bluetooth Gateway Part1: Installing Slackware

Power connector is micro USB on far right
I ordered in quick succession a NodeMCU ESP-12E and a Raspberry Pi Zero W. Both came very quickly but the Zero W got off the ground first.

I powered it using its micro USB socket (far right in above picture) with my favourite Slackware distribution in micro SD card, and nothing happened. It drew only 20-30mA current. I used a feisty 2A Samsung USB charger that put out 5.2V.

I had expected a little power LED at the very least. A little concerned I might have got a dud, I shorted the bare 'reset' PCB vias using a pair of steel tweezers.
'Reset' PCB vias (bottom left) marked 'RUN'
Again, nothing.  After 30 years of electronics you get used to having your blinkenlights, chief amongst them is your 'Power' LED.
The usual rats' nest

As a last resort, and just because I had a Sandisk Class 10 32GB microSD card, I loaded it with Ubuntu Mate, a recent Linux distribution for the Raspberry Pi B. And, bingo, after a few seconds I have my little green light. 

So instead of connecting a 'Power' LED directly to the power supply (usually in series with a current-limiting resistor) the designers must have used the CPU's GPIO pin to turn on the power LED. This kind of works, but makes it a little harder to troubleshoot power supply problems, and the Raspberry Pi has no shortage of those.

Nothing much happened after the power LED came on. No WiFi access point, no bluetooth device came up. Hmmm. I bought a mini HDMI adapter and hooked it up to my monitor and, aha, I have a 'kernel panic- Attempted to kill init!' message. Looks like Ubuntu Mate does not run on the Pi Zero W. This is confirmed by this handy article. Rather, the Pi Zero W (and its close relative the Zero) is compatible with the older Raspberry Pi A and A+.

Time to try my trusty Slackware 14.1 Raspberry Pi image. A few years ago I got Slackware running on the Raspberry Pi A by using an Ubuntu kernel with a Slackware root filesystem. I loaded it into my new microSD card, powered up, and nothing happened. No blinkenlight. Not even a kernel panic. Time to dig a little deeper.

A look at my Slackware image showed it consisted of 2 partitions, a Windows-type FAT32 boot partition and a much larger Linux ext4 root partition.

The Windows FAT32 boot partition looks like this:

$ls -l ../slackware/boot
total 31076
-rwxr-xr-x 1 root root     1447 Jan 19 15:29 LICENCE.broadcom
-rwxr-xr-x 1 root root     2013 Jan 19 15:29 README
-rwxr-xr-x 1 root root     1479 Jan 19 15:29 README.initrd
-rwxr-xr-x 1 root root   777094 Jan 19 15:29 System.map
-rwxr-xr-x 1 root root    16536 Jan 19 15:29 bootcode.bin
-rwxr-xr-x 1 root root       82 Jan 19 15:29 cmdline.txt
-rwxr-xr-x 1 root root       91 Jan 19 15:29 config.txt
-rwxr-xr-x 1 root root     5282 Jan 19 15:29 fixup.dat
-rwxr-xr-x 1 root root     2020 Jan 19 15:29 fixup_cd.dat
-rwxr-xr-x 1 root root      137 Jan 19 15:29 issue.txt
-rwxr-xr-x 1 root root  3112676 Jan 19 15:29 kernel.img
-rwxr-xr-x 1 root root  2104952 Jan 19 15:29 kernel_cutdown.img
-rwxr-xr-x 1 root root 16264692 Jan 19 15:29 kernel_emergency.img
-rwxr-xr-x 1 root root   275235 Jan 19 15:29 loader.bin
-rwxr-xr-x 1 root root  2011408 Jan 19 15:29 start.elf
-rwxr-xr-x 1 root root   523144 Jan 19 15:29 start_cd.elf
-rwxr-xr-x 1 root root      101 Jan 19 15:29 version-kernel_raspi.txt
-rwxr-xr-x 1 root root       92 Jan 19 15:29 version-raspi-boot.txt

And an ext4 Slackware 14.1 root partition:

$ls -l ../slackware/root
total 164
drwxr-xr-x  2 root root  4096 Jan 19 15:30 bin
drwxr-xr-x  2 root root  4096 Jan 19 15:30 boot
drwxr-xr-x 17 root root 69632 Jan 19 15:30 dev
drwxr-xr-x  3 root root  4096 Jan 19 15:30 doc
drwxr-xr-x 89 root root 12288 Jan 19 15:30 etc
drwxr-xr-x  6 root root  4096 Jan 19 15:30 home
drwxr-xr-x  7 root root  4096 Jan 19 15:30 lib
drwx------  2 root root  4096 Jan 19 15:30 lost+found
drwxr-xr-x 16 root root  4096 Jan 19 15:30 media
drwxr-xr-x 11 root root  4096 Jan 19 15:30 mnt
drwxr-xr-x  2 root root  4096 Jan 19 15:30 opt
drwxr-xr-x  2 root root  4096 Jan 19 15:30 proc
drwx--x---  9 root root  4096 Jan 19 15:30 root
drwxr-xr-x  2 root root  4096 Jan 19 15:30 run
drwxr-xr-x  2 root root 12288 Jan 19 15:30 sbin
drwxr-xr-x  2 root root  4096 Jan 19 15:30 srv
drwxr-xr-x  2 root root  4096 Jan 19 15:30 sys
drwxr-xr-t  5 root root  4096 Jan 19 15:30 tmp
drwxr-xr-x 20 root root  4096 Jan 19 15:40 usr
drwxr-xr-x 18 root root  4096 Jan 19 15:41 var

A quick look at the Ubuntu Mate image showed a similar organization, a FAT32 boot partition followed by a Linux ext4 root partition. The boot partition is:

$ls -l ../ubuntu_mate/flash
total 20432
-rwxr-xr-x 1 root root   18693 Jan 19 15:11 COPYING.linux
-rwxr-xr-x 1 root root    1494 Jan 19 15:11 LICENCE.broadcom
-rwxr-xr-x 1 root root   14273 Jan 19 15:11 bcm2708-rpi-b-plus.dtb
-rwxr-xr-x 1 root root   14010 Jan 19 15:11 bcm2708-rpi-b.dtb
-rwxr-xr-x 1 root root   13964 Jan 19 15:11 bcm2708-rpi-cm.dtb
-rwxr-xr-x 1 root root   15356 Jan 19 15:11 bcm2709-rpi-2-b.dtb
-rwxr-xr-x 1 root root   15992 Jan 19 15:11 bcm2710-rpi-3-b.dtb
-rwxr-xr-x 1 root root   15350 Jan 19 15:11 bcm2710-rpi-cm3.dtb
-rwxr-xr-x 1 root root   17932 Jan 19 15:11 bootcode.bin
-rwxr-xr-x 1 root root     223 Jan 19 15:11 cmdline.txt
-rwxr-xr-x 1 root root   36783 Jan 19 15:11 config.txt
-rwxr-xr-x 1 root root    6622 Jan 19 15:11 fixup.dat
-rwxr-xr-x 1 root root    2535 Jan 19 15:11 fixup_cd.dat
-rwxr-xr-x 1 root root    9753 Jan 19 15:11 fixup_db.dat
-rwxr-xr-x 1 root root    9753 Jan 19 15:11 fixup_x.dat
-rwxr-xr-x 1 root root 4130008 Jan 19 15:11 kernel.img
-rwxr-xr-x 1 root root 4231256 Jan 19 15:11 kernel7.img
drwxr-xr-x 2 root root    4096 Jan 19 15:11 overlays
-rwxr-xr-x 1 root root 2823396 Jan 19 15:11 start.elf
-rwxr-xr-x 1 root root  634532 Jan 19 15:11 start_cd.elf
-rwxr-xr-x 1 root root 4956996 Jan 19 15:11 start_db.elf
-rwxr-xr-x 1 root root 3906116 Jan 19 15:11 start_x.elf

In an old-fashioned desktop or laptop, the startup process is controlled by a non-volatile flash, eeprom or eprom program called the BIOS. The ARM CPU in the Pi has a much smaller BIOS and has the rest of it stored on the microSD card. Now since I got a kernel panic from Ubuntu Mate, this meant that the ARM CPU managed to load the kernel and run it, only to have the kernel fail later. Since it is the job of the BIOS to load the kernel, this mean my Ubuntu Mate image had the correct BIOS files.

This link provides a good summary: 

The boot sequence of the Raspberry Pi is basically this:

  1. Stage 1 boot is in the on-chip ROM. Loads Stage 2 in the L2 cache
  2. Stage 2 is bootcode.bin. Enables SDRAM and loads Stage 3
  3. Stage 3 is loader.bin. It knows about the .elf format and loads start.elf
  4. start.elf loads kernel.img. It then also reads config.txtcmdline.txt and bcm2835.dtb If the dtb file exists, it is loaded at 0×100 & kernel @ 0×8000 If disable_commandline_tags is set it loads kernel @ 0×0 Otherwise it loads kernel @ 0×8000 and put ATAGS at 0×100
  5. kernel.img is then run on the ARM.

The Ubuntu Mate image did not have loader.bin or bcm2835.dtb but it did have a kernel7.img in addition to the kernel.img file.  So I copied the files bootloader.bin, start.elf, kernel.img and kernel7.img over to my Slackware image, taking care to keep my Slackware config.txt and cmdline.txt. Since the Slackware image did not have any dtb files at all, I copied them as well.

Updated 2018-01-21. See next post for corrections to files list.

Now the image worked, and Slackware was able to start. A little problem is it did not recognize the WiFi hardware and its WiFi interface did not come up. But that is a problem for another post.


If you would like to run Slackware on your Raspberry Pi Zero or Zero W, leave me a comment in this blog post and I will put up the 8GB image for download.  

Happy Trails.

Thursday, 28 December 2017

IoT Bluetooth Autogate using Modbus


The IoT Bluetooth Relay Board tests well enough with a simple python script, but every time the script finishes bluetoothctl drops its connection to the HC-06. When you run the test script again, it reconnects to the HC-06 well enough, but the connection takes a variable number of seconds, and sometimes the script fails to connect. For an application like the autogate this is not acceptable.

However if the script does not exit the connection is reliable over many days, so the script should not exit. That means a server (Linux daemon) process, and its accompanying client script. There is one such python solution and that is pymodbus, a python implementation of Modbus client and server.

Modbus for an autogate? Modbus is a popular industrial communications protocol. Isn't that overkill?. It probably is.

Or is it? As we have seen the remote sensor/actuator portion is done using bluetooth device and a Microchip PIC18F14K50, which consumes 10mA at 5V. Modbus lives on the IoT gateway, ie the bluetooth master which happens to be an old laptop running Slackware Linux. Pymodbus makes my homebrew autogate remote opener compatible with IoT for Industry. The client python script will then be run by a PHP script from the laptop's apache webserver, in the same way as the Raspberry Pi Robot platform.

Security is via the WPA WiFi password on my home WiFi router. Notice that this system will still work without a broadband connection, as long as your smartphone is within range of the WiFi. The HC-06 will not accept a second bluetooth connection once it is paired to the laptop, so access is via the WiFi alone. With a ADSL connection this is a true IoT, able to accept commands from the Internet.

Remote autogate operation may make sense if you say, want to let the gardener into the yard, or the electricity/water utility person to read the meter. In my case my dogs will probably terrorize the meter reader   before galloping off to poop in the neighbour's yard. So its main advantage is an extended-range autogate remote.

The installation for Slackware is probably subject for another post, but say you had it installed. There is a sample program pymodbus-master/examples/common/updating-server.py which we can use as a template.

The first few lines of code are important:
#---------------------------------------------------------------------------#
# import the modbus libraries we need
#---------------------------------------------------------------------------#
from pymodbus.server.async import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer

#---------------------------------------------------------------------------#
# import the twisted libraries we need
#---------------------------------------------------------------------------#
from twisted.internet.task import LoopingCall

Next we import our bluetooth module, which shall look suspiciously like our previous python test script.

#---------------------------------------------------------------------------#
# communicate with bluetooth rs-485 pic18f14k50 2017-12-19
#---------------------------------------------------------------------------#
import autogate_bluetooth

The main server loop code is simply; we will add our bluetooth code later:
#---------------------------------------------------------------------------#
# define your callback process
#---------------------------------------------------------------------------#
def updating_writer(a):
    ''' A worker process that runs every so often and
    updates live values of the context. It should be noted
    that there is a race condition for the update.

    :param arguments: The input arguments to the call
    '''
    log.debug("updating the context")
    context  = a[0]
    register = 3
    slave_id = 0x00
    address  = 0x10
    values   = context[slave_id].getValues(register, address, count=5)
    values   = [v + 1 for v in values]
    log.debug("new values: " + str(values))
    context[slave_id].setValues(register, address, values)

Next there is our Modbus database, which we reduce from 100 to 16 to reduce memory usage, a initialize to 0 (except for the 2 bits reversed logic for the Arduino relays):
#---------------------------------------------------------------------------#
# initialize your data store
#---------------------------------------------------------------------------#
store = ModbusSlaveContext(
    di = ModbusSequentialDataBlock(0, [0]*16),
    co = ModbusSequentialDataBlock(0, [True, True, False, False]*4), # True because relay logic reversed
    hr = ModbusSequentialDataBlock(0, [i for i in range(16)]),
    ir = ModbusSequentialDataBlock(0, [45]*16))
context = ModbusServerContext(slaves=store, single=True)


Jumping to the end of the file for now, we have:
if __name__ == "__main__":
  #---------------------------------------------------------------------------#
  # run the server you want
  #---------------------------------------------------------------------------#
  looptime = 0.2 # 
  loop = LoopingCall(f=updating_writer, a=(context,))
  loop.start(looptime, now=True) # initially delay by time

  StartTcpServer(context, identity=identity, address=("localhost", 5020)) #2017-12-19

Note the loop time has been speeded up from 5s to 0.2s. StartTcpServer() is set to use the non-root network port 5020, which our client script will need to listen to.

That is all there is to it. Most of the pymodbus server code is done. Now for that bluetooth code in ./autogate_bluetooth.py, which I have put in the same directory so that it imports correctly. Here it is in full:

#!/usr/bin/python
import serial
from time import localtime, strftime, sleep

'''
Sends bluetooth rs-485 PIC18F14K50 commands and receives the replies.    
Usage:

import autogate_bluetooth.py
  ./autogate_bluetooth.py             to test
  Uses pyserial module
  The ems command is single-char, reply is a double sequence of 3 binary bytes
  followed by check strings
  Replies to 'z' are 2 3-byte analog input readings. 
'''

port1 = serial.Serial('/dev/rfcomm0',timeout= 1) #2017-12-19 HC-06 bluetooth   
print 'Waiting for bluetooth connect ...',
sleep(8) # Wait for bluetooth to connect
print 'Done'

cmd = ['z', '1', '2', '3', '0' ]
ChkStr1 = 'HCM'
ChkStr2 = 'LJP'

import struct
import binascii

def check_frame(reply):
  if reply[2:5] != 'HCM':
    print 'Checkbyte HCM Fail', reply
    return 0
  if reply[7:10] != 'LJP':
    print 'Checkbyte LJP Fail', reply
    return 0

  return 1
def ParseReply(reply):
  Battery_Voltage=int(format(ord(reply[0]), '02x'), 16)*256 + \
                  int(format(ord(reply[1]), '02x'), 16)
  Calibrated_Battery_Voltage = Battery_Voltage * 13.75 / 689
  PIC_Voltage=int(format(ord(reply[5]), '02x'), 16)*256 + \
              int(format(ord(reply[6]), '02x'), 16)
  Calibrated_PIC_Voltage = PIC_Voltage * 13.75 / 689

  return [int(format(ord(reply[0]))), int(format(ord(reply[1]))), \
          int(format(ord(reply[5]))), int(format(ord(reply[6]))) ]
  #return [Calibrated_Battery_Voltage, Calibrated_PIC_Voltage] 

def Send_Cmd(cmd):
  # sleep(1)
  port1.write(cmd)
  port1.flush()
  reply = port1.read(80)
  if reply != "":
    print 'Reply:', reply, 'len', len(reply)
  # for j in range(0, len(reply)):
  #   print format(ord(reply[j]), '02x'),
  # print '...'

    if (check_frame(reply) != 0):
      data = ParseReply(reply)
    else:
      data = 0
  else:
    print '... Noreply to cmd', cmd
    data = 0
  return data

import sys

# main program loop
if __name__ == "__main__":

  if (len(sys.argv) == 2):
    Send_Cmd(cmd= sys.argv[1])
  else:
    for i in cmd:
      answer = Send_Cmd(i)
      print 'result', answer

  port1.close()

The main function here is Send_Cmd(). The PIC18F14K50 has been programmed to accept the commands 'z' (read voltages), '0' (both relays on), '1' (first relay on), '2' (second relay on) and '3' (both relays off).

Now let us go back to updating_server.py so we can modify the main server loop to communicate with the HC-06. We need to add a new module update_output() to read the Modbus database. If there is a change (caused by the client script requesting the Arduino relay to turn on) it will then send the correct command to the HC-06. The code is:

import struct

def get_muxoutputBuf():
    register = 1      # digital output (modbus 'coils')
    slave_id = 0x00
    address  = 0
    values   = context[slave_id].getValues(register, address, count=8)#from 48
    # print 'values=', values
    return values


def get_muxscratchBuf():
    register = 1      # use holding registers for convenience
    slave_id = 0x00
    address  = 0x0008
    values   = context[slave_id].getValues(register, address, count=8)
    return values

def set_muxscratchBuf(values):
    register = 1      # use holding registers for convenience
    slave_id = 0x00
    address  = 0x0008 # used as scratch buffer
    context[slave_id].setValues(register, address, values)
    return values

def update_output(a): # new function 2016-08-10 09:15
    #context = a
    str = 'z'
    log.debug("Checking the output buffer")
    scratchBuf = get_muxscratchBuf()
    #print 'scratchBuf', scratchBuf
    outputBuf = get_muxoutputBuf()
    #print 'outputbuffer', outputBuf

    data = [0]*1
    #print 'data length', len(data),

    # pack the bits into the integer/byte array 2015-08-23
    for i in range(0, len(data)*8):
        j = i / 8
        k = i % 8
        # print 'j', j
        if outputBuf[i] == True:
            data[j] |= 1 << k
        else:
            data[j] &= ~(1 << k)

    datastr= [format(data[i], '02X') for i in range(0, len(data))]
    str = datastr[0][1] # 2017-12-20

    #print 'data is',data,'datastr',datastr,'output Cmd data string', str
    if (scratchBuf != outputBuf):
        autogate_bluetooth.Send_Cmd(str) # 2017-12-21 note answer is not stored
        print 'Hey, it is here!!!!'
        print '!!!!!!!'
        print '.......'
        print 'setting scratchBuf to', outputBuf
        set_muxscratchBuf(outputBuf)
        print 'scratchBuf', scratchBuf
    '''
    else:
        print 'scratchBuf', scratchBuf,'outputBuf', outputBuf
    '''

    return str

Next there is the code to update the Modbus database with the raw values read from the HC-06. These are the 2-byte values from the analog inputs. There are two inputs that makes 4 buyes in total.

def update_database(context, answer=[]):
    ''' Updated 2017-12-21
    raw = []
    for byte in answer:
      raw += byte
    print 'raw =', raw
    '''

    register = 3 # input register. *Not* 4
    address = 0; # 2017-12-20
    values   = context[0x01].getValues(register, address, count=4)# 2017-12-20
    print 'stored values=', str(values)

    log.debug(str(address) + " input register values: " + str(answer))
    print 'address=', address, ' answer: ', answer

    #context[0x01].setValues(register, address, raw) # 0x01 replaced slave_id
    context[0x01].setValues(register, address, answer) # 0x01 replaced slave_id

    return

battery_scan_interval = 3600 # set at 1 hour

The new server loop now becomes:

def updating_writer(a):
    global battery_scan_interval

    # read battery voltage every hour
    if battery_scan_interval == 3600:
        answer = [0*2] # 2017-12-19
        answer = autogate_bluetooth.Send_Cmd('z') # 2017-12-20
        if answer != 0:
            print '\n\n Cmd z, voltages are', answer
            update_database(context, answer)
            CPU_Voltage = answer[0] * 256 + answer[1]
            Calibrated_CPU_Voltage = (CPU_Voltage * 5.0) / 1024
            Battery_Voltage = answer[2] * 256 + answer[3]
            Calibrated_Battery_Voltage = (Battery_Voltage * 13.53) / 652
            print 'CPU', answer[0] * 256 + answer[1], 'Battery', \
                  answer[2] * 256 + answer[3]
            print 'CPU', "{:4.2f}".format(Calibrated_CPU_Voltage), \
                  'Battery', "{:4.2f}".format(Calibrated_Battery_Voltage),\
                  'Volts',strftime("%Y-%m-%d %H:%M:%S", localtime())

        else:
            print 'no answer'
        battery_scan_interval = 0
    else:
        battery_scan_interval += 1

    str = update_output(a) # 2017-12-20


And that is it. You run the server from a command line (remembering to run bluetoothctl and 'rfcomm bind /dev/rfcomm0' first) :

$python ./autogate_server.py
Waiting for bluetooth connect ... Done
Reply: ûHCM¥LJP len 10

 Cmd z, voltages are [3, 251, 2, 165]
stored values= [1, 2, 3, 4]
address= 0  answer:  [3, 251, 2, 165]
CPU 1019 Battery 677
CPU 4.98 Battery 14.05 Volts 2017-12-28 17:09:54
... Noreply to cmd 9
Hey, it is here!!!!
!!!!!!!
.......
setting scratchBuf to [True, False, False, True, True, False, False, True]
scratchBuf [True, False, False, True, True, False, False]
Reply: ÿHCM§LJP len 10

The client script is much simpler. Let's call it autogate_client.py. You only use the TCP/IP version:
#---------------------------------------------------------------------------#
# import the various server implementations
#---------------------------------------------------------------------------#
from pymodbus.client.sync import ModbusTcpClient as ModbusClient

You connect to the server thus:
client = ModbusClient('localhost', port=5020)
client.connect()

import sys
from time import localtime, strftime, sleep
if __name__ == "__main__":
    if len(sys.argv) == 2 :
        if sys.argv[1] == 'open' or sys.argv[1] == 'close' \
           or sys.argv[1] == 'Open' or sys.argv[1] == 'Close':
            coils = [True, False, False, False, False, False, False, False]
            rq = client.write_coils(0, coils, unit=0) # 2017-12-20
            print sys.argv[1], 'autogate'
            sleep(1);
            coils = [True, True, False, False, False, False, False, False]
            rq = client.write_coils(0, coils, unit=0) # 2017-12-20
        elif sys.argv[1] == 'ajar' or sys.argv[1] == 'Ajar':
            coils = [True, False, False, False, False, False, False, False]
            rq = client.write_coils(0, coils, unit=0) # 2017-12-20
            print sys.argv[1], 'autogate'
            sleep(1);
            coils = [True, True, False, False, False, False, False, False]
            rq = client.write_coils(0, coils, unit=0) # 2017-12-20
            sleep(2);
            coils = [True, False, False, False, False, False, False, False]
            rq = client.write_coils(0, coils, unit=0) # 2017-12-20
            print sys.argv[1], 'autogate'
            sleep(1);
            coils = [True, True, False, False, False, False, False, False]
            rq = client.write_coils(0, coils, unit=0) # 2017-12-20
        else:
            # python rocks! evaluate argument as python expression
            coils = eval(sys.argv[1])
            rq = client.write_coils(0, coils, unit=0) # 2017-12-20
            print 'writing', coils

        rr = client.read_holding_registers(0,4,unit=0x01)# 2016-12-20
        #print "read_holding_registers 1", rr.registers
        #print "read_holding_registers type", type(rr.registers)
        #print "read_holding_registers list data type", type(rr.registers[0])
        CPU_Voltage = rr.registers[0] * 256 + rr.registers[1]
        Calibrated_CPU_Voltage = (CPU_Voltage * 5.0) / 1024
        Battery_Voltage = rr.registers[2] * 256 + rr.registers[3]
        Calibrated_Battery_Voltage = (Battery_Voltage * 13.53) / 652
        print "read_holding_registers 1", rr.registers, 'CPU', \
              (rr.registers[0] * 256 + rr.registers[1]), 'Battery', \
              rr.registers[2] * 256 + rr.registers[3]
        print 'CPU', "{:4.2f}".format(Calibrated_CPU_Voltage), \
              'Battery', "{:4.2f}".format(Calibrated_Battery_Voltage),\
              'Volts',strftime("%Y-%m-%d %H:%M:%S", localtime())
    else:
        print 'Wrong/no arguments', len(sys.argv)

The main code is:
            coils = [True, False, False, False, False, False, False, False]
            rq = client.write_coils(0, coils, unit=0) # 2017-12-20
You set the bits you want (only the first 2 are implemented) and use client.write_coils() to write to the Modbus database. The server process does the rest. 

Typical output is:
$python ./autogate_client.py open
open autogate
read_holding_registers 1 [3, 251, 2, 62] CPU 1019 Battery 574
CPU 4.98 Battery 11.91 Volts 2017-12-28 16:14:06

To close the autogate you run the client again:
$python ./autogate_client.py open
open autogate
read_holding_registers 1 [3, 251, 2, 62] CPU 1019 Battery 574
CPU 4.98 Battery 11.91 Volts 2017-12-28 16:31:13

Now when I am on foot I find it useful not to open the gate wide, as a dog might then be tempted to bolt. This opens a man-size opening by triggering the relay twice:
$python ./autogate_client.py ajar
ajar autogate
ajar autogate
read_holding_registers 1 [3, 251, 2, 62] CPU 1019 Battery 574
CPU 4.98 Battery 11.91 Volts 2017-12-28 16:31:07

And that is all there is to it. Now for the PHP script.

$cat  ~/autogate/bluetooth/html/autogate.html 
   <!-- \/ starthtml -->
<html>
 <head>
   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
   <META NAME="keywords" CONTENT="Heong Chee Meng electronics engineer system sof
tware hardware digital design analog design FPGA VHDL parametric tester Windows N
T realtime device driver Linux kernel hacking semiconductor manufacturing SCADA e
mbedded Seremban Malaysia KM48T02 DAC71">
   <META NAME="description" CONTENT="Heong Chee Meng's Autogate Remote Control website.">
   <META NAME="author" CONTENT="Heong Chee Meng">
   <TITLE>Heong's Autogate Remote Control Website</TITLE>
 </head>
Heong's Autogate Remote Control Website

<p>
<p>
<p>
<p>
<p style="float: left; width: 33.3%; text-align: "center>
<form action="openbutton.php" method="post">
  <button type="submit" name="open" value="Connect"><img width="360" height="360" alt="Connect" src="./open.svg" align="left"></button> 
</form>
<p>
<p>
<p>
<p>
<p style="float: left; width: 33.3%; text-align: center">
<form action="openbutton.php" method="post">
  <button type="submit" name="ajar" value="Connect"><img width="360" height="360" alt="Connect" src="./ajar.svg" align="right"></button>
</form>

<p>
</BODY>
</html>
<!-- /\ end html  -->

And the PHP script is:

$cat  ~/autogate/bluetooth/html/openbutton.php 
<html>
<body>
<article>
<?php
  if (isset($_POST['open'])) {
    $result = shell_exec('python autogate_client.py open');
    echo "Done!<pre>$result</pre>";
  }
  if (isset($_POST['ajar'])) {
    $result = shell_exec('python autogate_client.py ajar');
    echo "Done!<pre>$result</pre>";
  }
  header("Location: ./autogate.html");
?>
</article>
</body>
</html>

Aim your browser (I used Google Chrome) at the webserver: xx.xx.xx.xx/autogate/autogate.html and there you have it- an IoT bluetooth Autogate remote. 

Happy Trails

Saturday, 23 December 2017

IoT Bluetooth Relay Board for Autogate

Clockwise from top: Arduino dual relay, LM2956 boost-buck DC-DC converter, bluetooth USB PIC18F14K50 and HC-06 bluetooth module
The IoT Autogate is simply a combination of two systems, the remote-controlled Autogate and the Bluetooth Solar Battery Voltmeter. with the addition of a cheap (only RM10) Arduino dual-channel relay board.
Arduino 5V opto-isolated dual relay PCB
The PIC18F14K50 (it is really an incarnation of the Microchip Low Pincount Development Kit)  can drive the optoisolator input directly using two of its spare pins configured as digital output. Best of all the relay coil consumes little power: 80mA per active relay. When it is not active the power used is 10mA at 5V or 0.05W. This is very useful because in the event of a power cut and the autogate is running on battery, you want the gate to remain powered for as long as possible.

The relay board logic is reversed, that is a 'low' or 0V output by the PIC18F14K50 turns the relay on. I happened to have the pins RC4 and RC5 free so I used them. This is the same port as the voltmeter's analog input pins (RC6 & RC7).

I first tested it from the USB port of my laptop. This lets me start testing from a clean USB Microchip Low Pincount Development Kit software and helps prevent errors in my bluetooth code from affecting the relay board code. It is safer to use a USB hub, preferably one which has its own 5V power in case a wiring mistake damages your laptop.

First of, simply set TRISC correctly for both analog inout and digital output:
    #define mInitAllLEDs()      LATC &= 0xC0; TRISC &= 0xC0;
If you need to delve further into this code you first need to read the (rather thick) PIC18F14K50 datasheet.

We put a little veneer of C over the new analog output bits:
    #define digital_output_1    LATCbits.LATC4 // 2017-12-03
    #define digital_output_2    LATCbits.LATC5 // 2017-12-03

So, to turn both relays off (like on power up) we simply do:
    digital_output_1 = 1; //Make sure relays are off
    digital_output_2 = 1; //Make sure relays are off

I simply added a counter to the USB code and wrote the value of the counter to the PIC18F14K50 output port. The first time a voltmeter command is issued, only one relay comes on (01). The second command causes the first relay to go off and the second relay to come on (10). The third command causes both relays to go off (11). The forth command causes both relays to come on (00).

The actual test is simplicity itself (python rocks!) and took just a few minutes. The relays duly clicked in their assigned order:

$python
Python 2.7.11 (default, Mar  3 2016, 13:35:30)
[GCC 5.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import serial
>>> port=serial.Serial('/dev/ttyACM0', 19200, timeout=1)
>>> port.write('z');port.read(20);
1
'\x00\x03HCM\x00\x00LJP\x00'
>>> port.write('z');port.read(20);
1
>>> port.write('z'); port.read(50);
1
'\x02/HCM\x01}LJP\x00'
>>> port.write('z'); port.read(50);
1
'\x03\xd1HCM\x03eLJP\x00'

Now with the new digital output function working it is time to test it with the bluetooth code. To do so I needed to power on from a non-USB 5V. Otherwise the PIC18F14K50 would detect the USB port and would not run the bluetooth code. This is easily done with a power bank.


 

As before, use hciconfig and bluetoothctl.

[bluetooth]# devices
[bluetooth]# paired-devices
[bluetooth]# power on
Changing power on succeeded
[bluetooth]# scan onm
Invalid argument onm
[bluetooth]# scan on
Discovery started
[CHG] Controller C8:FF:28:27:7D:2C Discovering: yes
[NEW] Device 98:D3:32:20:BB:7B HC-06
[bluetooth]#
[bluetooth]# agent on
Agent registered
[bluetooth]# default-agent
Default agent request successful
[bluetooth]# pair 98:D3:32:20:BB:7B
Attempting to pair with 98:D3:32:20:BB:7B
[CHG] Device 98:D3:32:20:BB:7B Connected: yes
Request PIN code
[agent] Enter PIN code: 1234
[CHG] Device 98:D3:32:20:BB:7B UUIDs: 00001101-0000-1000-8000-00805f9b34fb
[CHG] Device 98:D3:32:20:BB:7B Paired: yes
Pairing successful
[CHG] Device 98:D3:32:20:BB:7B Connected: no
[bluetooth]#

Then 

$rfcomm unbind /dev/rfcomm0 98:D3:32:20:BB:7B 1
$rfcomm bind /dev/rfcomm0 98:D3:32:20:BB:7B 1

Back to python (did I say python rocks?)

$python
Python 2.7.11 (default, Mar  3 2016, 13:35:30)
[GCC 5.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import serial
>>> port=serial.Serial('/dev/rfcomm0', timeout=3)
>>> port.write('0');port.read(100)
1
'\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x0
3\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xf
bHCM\x009LJP\x03\xfbHCM\x009LJP'
>>> port.write('1');port.read(100)
1
'\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x0
3\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xf
bHCM\x009LJP\x03\xfbHCM\x009LJP'
>>> port.write('2');port.read(100)
1
'\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x0
3\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xf
bHCM\x009LJP\x03\xfbHCM\x009LJP'
>>> port.write('3');port.read(100)
1
'\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x0
3\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xf
bHCM\x009LJP\x03\xfbHCM\x009LJP'
>>> port.write('0');port.read(100)
1
'\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x0
3\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xfbHCM\x009LJP\x03\xf
bHCM\x009LJP\x03\xfbHCM\x009LJP'
>>>

Finally, I needed a 12V to 5V DC to DC power supply. The autogate runs off a 12V backup battery, and while I could wire to the autogate controller's 5V (it is also a Microchip) that would mean soldering two wires to every time I changed controllers. Again Arduino provided the answer: the LM2596 module that cost RM3.50 on sale.


Do power it up and set the output before you connect it to your PIC18F14K50, for the LM2596 is a step-up (boost) as well as step-down (buck) converter. The trimpot is multiturn, so it might take a bit of turning to get it down to 5V. Hook it up to the PIC18F14K50 and we are ready to mount it into the autogate

You can go faster and jump right to the bluetooth section, but I find while these methodical baby-steps may take a little longer, it sure beats two weeks of confusion while you sorted out the interacting bugs from hardware build, digital output code and bluetooth code. 

Perhaps you can spot the bug in my code, which I will address in the next post. 

Happy trails.