A denser LLAP serial RF data format

Ciseco’s LLAP format is a nice lightweight and PIC microcontroller and Arduino friendly serial protocol. I use their XRF modules for RF communication, these support power-down so they are well-suited to intermittent operation off a battery. Standing current on receive is 23mA so continuous operation is more of a challenge, for instance at the RF to SMS gateway. It has 12 bytes like so:

LLAP Message format

Each message is exactly 12 characters long and in three distinct sections:

  • One start byte
  • Two bytes for the device identifier
  • Nine data bytes, padded by dashes if necessary.

<SB> <ID> <ID> <D> <D> <D> <D> <D> <D> <D> <D> <D>

See Appendix 3 for details of the permissible characters in each field.

Their examples, however, send only one data value per LLAP message, with a descriptive section. Hence

aAATMPA12345

Which is wasteful IMO. A lot of sensors have two data points,for instance temperature difference measurements, or temperature and relative humidity.

Few real world sensors  can justify the precision of using all the digits; I don’t have any with an accuracy of more than three digits. Sensing temperature to an accuracy of 0.1C is unusual – the popular dalas 18B20 is accurate to 0.5C but to do much more implies a piece of laboratory equipment. Useful values of temperature in the UK would be -20 to 120 °C, Relative humidity is 0 to 100 – cheap sensors don’t really justify a .x so allocating four digits covers most bases. Negative values give the ugly -21. as the – takes up a digit but it’s only a machine that sees it. So I can make a double density device as

aAA12.34X5.67

and keep within the spec. I use **** for failed or missing sensors, and the X is replaced by L,M or H for battery status. M and H are operational, L means may be about to switch off in a few cycles. In sensors that support H then M means would still accept charge, H is enough. However I use a simple comparator at about 4.4 V on a PIC 16F628 so I can only show L and M.

This saves me precious power, and allows me to consolidate two temperature sensors to one radio saving cost of the radio and aggravation of maintaining batteries.

 

two-sensor PIC and XRF device
two-sensor PIC and XRF device

I couldn’t use JAL for this because I laid out the board to use the 16F628’s internal oscillator that runs at 4MHz and the JAL one-wire lib wants to run at 20MHz. So I had to code it in assembler 🙁 Next time I’ll leave space for a 20MHz resonator on the board that will save me all that grief.

I now get to read two temp sensor and the battery status, all in one LLAP message 🙂

 

 

How to embed a Xively chart image

Note: I don’t do this any more. Third party APIs are a world of hurt – you save some time up front and end up chasing someone else’s upgrade path on their agenda, supporting their ads. There’s nothing fundamentally wrong with building cloud services and dependencies into your work, as long as you don’t want it to be up there for more than six weeks in my experience.

In their increasing commercialisation as Pachube went away from the original hacker ethos to Cosm, and now Xively, Xively have totally borked the documentation, at least for freebie cheapskates.

The most obvious thing you want to do with an IoT device is to chart the time series. With Pachube and Cosm that was easy. It’s also pretty easy using Xively, but what isn’t easy is finding out how to do it!

 

I don't use Xively any more so this is static
I don’t use Xively any more so this is a static capture from some time ago

This is what I’m after. I have a Geiger counter, and a PIC ticks up a counter each time it detects an event. After a minute is up, it reads the counter, sends it via LLAP over a serial RF network to a Raspberry Pi that uploads the count to Cosm Xively and resets the counter, and it all starts over again.

Note: I’ve now moved to running RRDtool on the Pi because I found Xively just a little bit too intermittent, plus there’s the whole getting control of my own data thing. I found it more reliable to store the data locally, generate the graph and then upload the graph. It isn’t necessarily Xively’s fault – I get the feeling  my ADSL connection just isn’t reliable enough for something that needs to update every minute,

RRDtool looks a bit more grungy, though I kinda like that here –

A little bit more grungy from RRDtool, but more reliable in my application
A little bit more grungy from RRDtool, but more reliable in my application

Anyway, back to Xively, and how easy graphing from Xively worked for me –

It was easy to find out how to do it on Pachube and Cosm but because Xively is all about provisioning IoT bits rather than hackers’ web charting now it’s the devil’s own job to find out how to do it. Xively favour stackexchange for support nowadays, but they can be less than helpful to n00bs or those who do not show that they feel The Force to the required extent 😉

Here is what I did to embed a Xively chart:

Read this from Xively on how to embed a chart – it’s not linked from any obvious place.

https://xively.com/dev/docs/api/data/read/single_datastream/

It so happens that the png chart option is a specific instance of reading a single datastream. You, me, and everybody else asks Google ‘how do I embed a Xively chart image’. Xively think of this in terms of ‘how do I display as single datastream’ and they’d rather you not do that, but parse the data returned from the feed, so they hide the relevant page in a backwater of their documentation because they don’t see it as important. Which is entirely their right – after all, you get the support you pay for!

Then import the image like so

https://api.xively.com/v2/feeds/YOUR_FEED_ID_HERE/datastreams/YOUR_CHANNEL_NAME_HERE.png?width=730&height=250&colour=F15A24&timezone=UTC

obviously fiddle with width height, colour to taste. Note that it doesn’t need you API KEY, because presumably a PNG isn’t about to start posting data back to your feed any time soon. One of the more useful extra items is the duration=3hours

which doesn’t happen to be one of the official parameters in the Xively historical parameters documentation, but it is in the example they give and is a useful undocumented feature. A shorter duration works better with the Geiger count.

Your device needs to be public access if you want other people to see it, natch 😉 Make it private and they will be invited to log in on Xively, which sort of defeats the point.

Now why did that have to be so hard?

Alternatives ways of showing Xively data

The embedded Xively chart is loaded when you load your page, it doesn’t auto-refresh like the display on your Xively Workbench. That’s fine for many things, but it is very Web 1.0 so if you want more then take a look at Dave’s method of using Google charts by getting the raw data back from Xively. That way you get a more responsive chart and can plot more than one thing at a time, but you are looking at a lot more work compared to the quick-and-dirty method of an embedded image.

how to measure AA battery capacity

Poundland in the UK sell batteries branded by Kodak, but the cheapest ones are zinc chloride. I thought ZnCl has gone out of use decades ago, but it is surprising how often people are penny-wise and pound foolish. Particularly in pound shops 😉

Cheap Poundland batteries are in fact twice as dear to use!
Cheap Poundland batteries are in fact twice as dear to use…

So I am looking to make something to track the discharge of a battery. This is also useful to qualify NiMH and NiCd batteries – these fade with time and it is good to know what sort of capacity is left.

...as the expensive ones!
…as the expensive ones!

Every minute it reports the voltage and current from the batteries running through a 2.5V torch bulb, the third bulb is maintained at 2.5V to provide a reference. It transmits the signal using radio to a datalogger. I got a camera to take a picture every 15 minutes, as a video the results are reasonably clear. A picture tells a thousand words – the Zinc Chloride batteries are crap

The left-hand bulb is powered by the ‘cheap’ battery that Poundland sell for 9p, the middle is powered by the ‘dearer’ alkalines they sell at about 17p.

Bulb measurement schematic
Bulb measurement schematic

I have a Ciseco OpenKontrol Gateway as a datalogger. It stores data to a SD card, but there is no law saying you couldn’t send it to some kind of IoT host like Xively. So all I need is the interface to the batteries to track the voltage and current; I used a 2.5V torch bulb for the load because it does not seem unreasonable to me to expect batteries to be used in a torch, I use two batteries in series, which seems to be the most common configuration in a torch. On the way to ground I use a 1 ohm resistor so I can measure between ground and the top of the bulb the battery voltage and between the resistor and ground the voltage corresponding to the current. Using 1 ohm makes the calculation easy, and also drops about 0.3V so the bulb is run at 2.7V at the start.

Now if you are looking to do this in a big way then these guys at Battery Showdown are doing this is a more rigorous way but their hardware is dearer. I’m also not absolutely sure that a constant current discharge is typical of many loads. something like a digital camera with a switch mode PSU actually increases its peak load with decreasing battery voltage because it is more a constant power device and needs to draw more current at a lower voltage to run the focusing bits. Something with a linear voltage regulator is a constant current to the dropout voltage of the regulator, but you don’t often see linear regulators in battery-powered kit.

The bulb isn’t ideal either, with a varying resistance with temperature, so I’d probably favour using resistive loads, and getting four/five battery channels instead of two. But the visuals were great for the video.

If you have a PC that can take RS232 then simply wire the output of the PIC to the PC serial port via a Max232 chip or single transistor inverter if you don’t need the radio connection. Since I have the datalogger logging other sensors it seems easy enough to use it as is.

I used a PIC16F870 that has five A/D channels – this gives me two battery channels. Each battery channel has one A/D port connected to A in the diagram, another one to B and 0V is connected to C. The voltage at B is the same as the current since R is 1Ω and the voltage at A is the battery voltage itself. The 10-bit A/D converter goes up to 1024, by setting the reference voltage to be the PIC power supply at the high end and PIC ground at the low end I made the maths easier by setting the PIC supply voltage to 5.12 V (this is within the PIC operational voltage spec of 4 to 5.5V) in which case a straight divide by two gives V in * 100, and matches the requirements acceptably.

Doing this for the Poundland batteries shows the difference between the alkaline and the ZnCl batteries

alkalines give about three times the runtime than ZnCl
alkalines give about three times the runtime than ZnCl

If we take the service life as down to 2V from 3V initially, then you get 1.7 hours from the cheap ones and 5.8 hours from the alkalines.Alkaline cost/hr is 17*2/5.8=5.9p/hr and the cheap batteries cost 9*2/1.7=10.6p/hr.

As a result you pay about twice as much to run something using the cheap Poundland batteries, and you get to replace the batteries three times as often. Saving money therefore costs you a fortune in aggravation and generates more waste. You also have this sort of problem if you don’t pay attention and leave the used batteries in too long.

cheap batteries consume the zinc outer casing, leading to this
cheap batteries consume the zinc outer casing, leading to this

The device offers an insight into the capacity of rechargeable batteries which is the more useful feature of it.

a very old pair of 500mAh nicads and a pair of Uniross 2300mAh NiMh
a very old pair of 500mAh nicads and a pair of Uniross 2300mAh NiMh

I am not really sure I understand the pathology behind the second wind the NiMH cells seem to have. I’d discard it as a fault in reading except that – the current shows the bulb did not blow or become loose, and the A/D and voltage reference is multiplexed, so the error should have shown elsewhere.  will repeat this and see if it happens again – probably not as this battery has been totally flattened so it is not the same battery.

For rechargeables I will modify this rig to take four individual cells and discharge through four resistors. That way I don’t back-charge any weak cells, and I don’t need to measure the current as the terminal voltage and Ohms Law will tell me the current. If I run 2.7Ω I will run about 440mA.

 

 Schematic

PIC battery tester schematic

JAL code

The JAL code for the PIC is 1311_datalogger_v06.jal

-- JAL 2.4i
-- derived from univ-sensor_v06
-- Datalogger
-- adjust power supply to 5.12V using the LM317 regulator.
-- internal Vref = VCC = 5.12V
-- Can just divide ADC by 2 and shift dec point by two places
-- v06 12 Nov 2013 RM
-- CC BY-SA 3.0
-- http://creativecommons.org/licenses/by-sa/3.0/
-- serial port designed for use with a Ciseco XBBO board and radio ( Google it)
-- can also use w/o radio via a Max232 or a FTDI adapter instead
-- radio is powered down while ADC measurement made to avoid loading regulator voltage

include 16f870
pragma target clock       4_000_000
pragma target osc    HS   ; use a 4MHz crystal or resonator
pragma target WDT    ENABLED
pragma target PWRTE  ENABLED
pragma target BROWNOUT    ENABLED
pragma target LVP    DISABLED
pragma target CP    DISABLED
pragma target CPD    DISABLED
-- HS, -BOD, ,-LVP, -WDT, -CP  = 0x3F22
;pragma target fuses       0x2007

include pic_general
include print
include format

-- set all interrupt sources disabled
;INTCON = 0

-- ADC includes delays so don't include this myself

-- V2 delay's
-- include delay_any_mc

-- more delay functions
-- include extradelay

-- the analogue pins
pin_a0_direction = input
pin_a1_direction = input
pin_a2_direction = input
pin_a3_direction = input
-- there is no a4 analogue channel
pin_a5_direction = input

pin_c7_direction = input
pin_c5_direction = output
pin_c6_direction = output

pin_A4_direction = output          ; this is open drain so 3V3 pullup comes from XRF
alias xrf_sleep is pin_A4          ; pull this low to start the XRF, let go and float up to enter sleep
xrf_sleep=low                      ; must enable with ATSM2

-- now configure ADC  (this is a 10-bit ADC)

const bit ADC_HIGH_RESOLUTION = true
const byte ADC_NCHANNEL = 1
const byte ADC_NVREF = ADC_NO_EXT_VREF
include adc
adc_init()

-- ok, now setup serial, we'll use this
-- to get ADC measures

-- all this is because the 16f870.inc file is not right for serial hardware  8/5/13
alias RCIF IS PIR1_RCIF
alias TXIF IS PIR1_TXIF
alias BRGH IS TXSTA_BRGH
alias RCIE IS PIE1_RCIE
alias TXIE IS PIE1_TXIE
alias TXEN IS TXSTA_TXEN
alias TRMT IS TXSTA_TRMT
alias SPEN IS RCSTA_SPEN
alias OERR IS RCSTA_OERR
alias CREN IS RCSTA_CREN
-- end of
-- all this is because the 16f870.inc file is not right for serial hardware

alias PEIE IS INTCON_PEIE
alias TMR1IE IS PIE1_TMR1IE
alias CCP1IF IS PIR1_CCP1IF

OPTION_REG = 0b00001111 ; PS asllocated to WDT and slowest rate selected (20ms*128)
PEIE = high
TMR1IE = high
T1CON=0b00110001        ; set timer 1 running internal osc 8xprescaled
CCP1CON=0b00001011      ; reset tmr1 on match, kicks off an A/D conversion
                        ; these next values seem to call a period of 10s from previous project
CCPR1H=244              ; decimal
CCPR1L=36               ; decimal
-- so count is 244*256+36 = 62500 At 4MHz each tick is 1uS, prescsaled by 8 to 8uS ticks.
--
const byte cyclesmax=60*2   -- how many times to count 0.5s ticks

var word mincounter = 0
;var byte lastTMR0=0
;TMR0=0   -- clear at start

-- ok, now setup serial;
const serial_hw_baudrate = 9600
const bit usart_hw_serial = TRUE
const bit lowspeed=true
include print
include serial_hardware
serial_hw_init()

const byte booted[] = "aVVSTARTED--"
const byte prefix[] = "aVV"
const byte line1[] = "+++"
const byte line2[] = "ATSM2r"
const byte line3[] = "ATACr"
const byte line4[] = "ATDNr"
const byte line5[] = "ATID0AFFr"
const byte vpfx[]= "VLT"

procedure WaitOnOk() IS
-- state machine - aim is to hang and
-- if this doesn't get to 2 the WDT will start things over after 2 sec
-- if it gets to 2 it clrwdt anbd returns

var byte state=0
asm clrwdt
while state !=2 loop
if serial_hw_data_available then
   if state==0 & serial_hw_data == "O" then
      state=1                        ; this is the only way to get to state 1
   end if
   if (state == 1) & (serial_hw_data == "K") then
      state=2
      else
      state=0                        ; because O was followed by something that wasn't K
   end if
end if
asm clrwdt
end loop

end procedure

procedure justify(word IN myval)  IS
-- value is going to be 0..1024 (in reality 0..512)
-- hundreds are integers, ie 2 d.p.
-- assumes radio is up and running
  format_word_dec(serial_hw_data,myval,3,2)  -- 0   .23
  serial_hw_data="-"
end procedure

function get_volts() return bit  is
  var word reading1
  var word reading2
  var word reading3
  var word reading4
  var word reading5
  asm clrwdt
  -- get ADC result, high resolution
  reading1 = adc_read_high_res(0)/2
  asm clrwdt
  -- get ADC result, high resolution
  reading2 = adc_read_high_res(1)/2
  asm clrwdt
  -- get ADC result, high resolution
  reading3 = adc_read_high_res(2)/2
  asm clrwdt
  -- get ADC result, high resolution
  reading4 = adc_read_high_res(3)/2
  asm clrwdt
  -- get ADC result, high resolution
  reading5 = adc_read_high_res(4)/2
  asm clrwdt
  -- send it back through serial
  xrf_sleep=low                                    -- start the radio a little before the data will be sent.
                                                   -- may want to change the LLAP prefix and comma del if not using radio
  delay_100ms(2)
  asm clrwdt
  print_string(serial_hw_data,prefix)
  print_string(serial_hw_data,vpfx)
  serial_hw_data="A"
  justify(reading1)
  asm clrwdt
  delay_100ms(5)                                   -- no need for this dela if going straight out on serial
  print_string(serial_hw_data,prefix)
  print_string(serial_hw_data,vpfx)
  serial_hw_data="B"
  justify(reading2)
  asm clrwdt
  delay_100ms(5)                                  -- allows the LLAP data to be received properly
  asm clrwdt
  print_string(serial_hw_data,prefix)
  print_string(serial_hw_data,vpfx)
  serial_hw_data="C"
  justify(reading3)
  asm clrwdt
  print_string(serial_hw_data,prefix)
  print_string(serial_hw_data,vpfx)
  serial_hw_data="D"
  justify(reading4)
  asm clrwdt
  delay_100ms(5)
  asm clrwdt
  print_string(serial_hw_data,prefix)
  print_string(serial_hw_data,vpfx)
  serial_hw_data="E"
  justify(reading5)
  asm clrwdt
  delay_100ms(2)                                         -- let the signal be transmitted and buffer empty
  xrf_sleep=high                                   -- stop the radio ftre the original data sent
  return true

end function

-- MAIN PROGRAM START

asm clrwdt
asm sleep                           ; use the WDT to delay about 2s to
                                    ; boot the sensor  and radio
asm clrwdt

-- now initialise the XRF to the system network ID and enable sleep mode
-- clear out any pre-existing RX buffer
var byte rxd
while serial_hw_data_available loop -- check if data is ready for us
  rxd = serial_hw_data          -- get the data
end loop
rxd=" "

if true then                        ; loop is simply to enable/disable
                                    ; if you have no radio then use if false then
asm clrwdt                          ; this setup routine for testing
xrf_sleep=low
                                    ; setup the radio using AT commands
print_string(serial_hw_data, line1) ; send +++
WaitOnOk
print_string(serial_hw_data, line5) ; change network with ATID0AFF
delay_100ms(1)
print_string(serial_hw_data, line2) ; enable sleep mode with ATSM2
delay_100ms(1)
print_string(serial_hw_data, line3) ; and use it - send commit
delay_100ms(1)
print_string(serial_hw_data, line4) ; ATDN we are done configuring
delay_100ms(10)
print_string(serial_hw_data,booted)
delay_100ms(2)
xrf_sleep=high                                    -- power down the radio
end if

forever loop

asm clrwdt
if CCP1IF then
   CCP1IF=false;
   mincounter=mincounter+1
end if

if mincounter >= cyclesmax-1 then
   mincounter=0
   var bit b = get_volts()
end if

end loop

 

AM2302 (DHT22 ) Temp Humidity sensor and JAL on a 16F628 at 4MHz clock

Having decided I can’t be bothered with digital sensors with oddball serial interfaces like the DHT22 it was time to suck it up when I needed a number of sensors. Cost adds up with lots of sensors – though that Honeywell product more than paid for itself a few times over in much better hatch rates (fertile eggs are about £2 a pop by post, that’s how much of a loss you eat for every failure to hatch!) not every sensor application affects the bottom line like that. Sometimes low cost trumps accuracy, reliability and serviceability. Enter the AM2302, apparently a.k.a. DHT22, produced by the fine Aosong corporation. Their website looks like line noise on my browser, but apparently they are based in Guangzhou, which is China’s third largest city, a conurbation of nearly thirteen million souls.

AM2302, humidity and temperature sensor, a.k.a. DHT22 apparently
AM2302, humidity and temperature sensor, a.k.a. DHT22 apparently

The sensors are cheap, nasty and have poor accuracy, but the price is right, it’s the cheapest way to get a humidity and temperature sensor. Five for £17.70 or a unit price of £3.54 from a Chinese supplier on ebay, Buyincoins ISTR. They have a non-standard one-wire interface. That requires you to be able to tell a 30μS high duration from a 68μS high duration. No problem, eh, even with a PIC running on the internal 4MHz oscillator so each clock cycle is 1μS?

There was already a JAL library for this, called temperature_humidity_dht11.jal so I am in development heaven.

Except it doesn’t work – it acts up after about 20s in the video. It sort of works some times, tantalising short runs of OK in amongst loads of timeout errors. I fiddle with the power supply a little as the AM2302 is claimed to be finicky on the need for 5V. No luck. Tracing the library code I find it barfs around

Continue reading “AM2302 (DHT22 ) Temp Humidity sensor and JAL on a 16F628 at 4MHz clock”

World Listening Day 2013

It’s R. Murray Schafer‘s birthday today. In 1973 he research the Vancouver soundscape, later extending it to compare five European villages from a soundscape point of view.

The research became the basis of ‘Acoustic Ecology’, a discipline that R. Murray Schafer developed to further investigate ‘soundscapes’, which are understood as the sonic interface between living beings and their environment.

World Listening Day is held on his birthday to celebrate Schafer’s contribution to the art of listeing to the world, rather than just hearing it. I’ve usually aimed to try and isolate sounds, other than in the lo-fi urban environment where you just can’t do that. However, in tribute to R. Murray Schafer’s ideas, I had a go, starting off with the birds at dawn. It’s a bit past the time for the classic dawn chorus, but these birds in a semi-rural location in Rushmere made a decent attempt at a soundscape for me.

XY recording

Ipswich Marina
Ipswich Marina

For a change I tried an urban field recording at Ipswich Marina, this recording starts with oystercatchers at the beginning, to the right is the sound of some construction work that has been restarted after a couple of years. A woman in a RIB motors to her boat moored somewhere in the marina which is mainly to the left. Some foot and bicycle traffic passes. The waterfront has been redeveloped for leisure over the last decade.

Binaural recording with Soundman OKMII

Finally I gave in to the separator in me and recorded the sound of this tarmac laying crew and their machine, in particular the backing up sound.

The reversing sound is an electronic noise played through speaker, which highlighted one of the issues R. Murray Shafer picked up – Continue reading “World Listening Day 2013”

Leeds Victoria Quarter covered arcade

Des Coulam of Soundlandscapes had warmed me up that glass-covered markets had a great ambience. He has a whole section dedicated to the Parisian passages-couvertes so I was chuffed to find this one on a visit to Leeds – the old Victoria Quarter.

Swifts on a warm summer evening

Swifts are one of the fantastic soundmarks of summer, and they sound at their best in the city, with their high-pitched screaming resonating from the houses all around. You get them in rural parts too, but the sound needs the hard surfaces of the city when they come in low at rooftop height in the warm summer evenings. According to the BTO they like towns.

The Devil’s Bird is the devil’s own job to record, too. You don’t try and track them, there’s just no hope to get anything directional on the job, and the screaming groups tend to spread out as they get close too. Just don’t even think of using a parabolic dish or a shotgun mic 😉

This one is basically the Olympus LS-10 with internal mics propped in a first-floor window, and snipped out of a long trawl for swifts, Then I used a parametric EQ to hit some of the town traffic rumble.

Goods Train from Felixstowe, Ipswich St Mary’s

I have some time to do more recording now. Okay, so it’s not hyper-original recording trains but I liked the screech of the wheel flanges as it rounds a fairly gentle bend. I was at the same level as the track across a dip due to the lie of the land

Also a chance to see how this Audioboo thing works… which seems to be pretty well 2018 update – they decided to start charging $9.99 a month. You must be kidding, guys, I may as well pay WordPress £33 a year to be able to get audio facility. There’s no low-end offering.

16F628 PIC A/D using JAL

The 16F628 pic doesn’t actually have a A/D converter. It has a couple of comparators and a software controllable voltage reference which does pretty well for most applications. I’m looking at using it for a low-battery cutoff device to protect 12V lead-acid batteries.The two comparators would work for that – one for the low-water mark, and one set a little bit above for a level the charge needs to reach before the load is reapplied, with some time delay probably too.

However, I came across Microchip’s Application note AN700 Make a Delta-Sigma Converter Using a Microcontroller’s Analog Comparator Module which shows how to use the comparator as an A/D  converter. There are plenty of PICs that already have an A/D converter – the 16F88 has several analogue channels, but the 16F628 is cheaper. A lot of applications need just one analogue value sampled, and my application also wants an A/D that includes 12V. If I used a chip with an onboard A/D I’m going to need two resistors anyway to pad 12V down to the 5V range, and AN700 fig 7 shows how to take advantage of the external parts to shift the A/D range up to about 12V.

Trouble is, I want to use JAL, to make life easier when I write the rest of the code, and the assembler mode of JAL didn’t work. I swiped this from here probably derived from here but it didn’t work for me. I took a look at the code and suspected the assembler part, because what was happening was as soon as I called the DeltaSigA2D() the whole program seemed to sit down and start again from the top, resulting in endless printing of “STARTED” and little else.

Thinking about what JAL is probably doing with assembler, I suspected some of the clever space-saving constructs like the bit in eat5cycles that goes

goto +1

which was supposed to be

goto $+1

could be asking for trouble unless I could really assume that next instruction is the next one in the program space. In the end if you’re going to use something like JAL you aren’t trying to eke out every last drop of code space, so I figured I would nut the cleverness and use stacked nops. And it worked.

 

-- JAL 2.4i
-- Just the A/D 15 June 2013
-- modded by Richard
--
-- adc_test_16f628_v03
-- after DeltaSig.asm v1.00 from Dan Butler, Microchip Technology Inc. 02 December 1998
-- after http://den-nekonoko.blog.so-net.ne.jp/2010-11-25 and AN700
-- continually spews 10-bit value on TXD at 9600 baud, 5V=1024 0V=0
-- AN700 Fig 2
include 16f628_inc_all
-- We'll use internal oscillator. It work @ 4MHz
pragma target clock       4_000_000
const word _fuses         = 0x3fff ; default value
pragma target osc         INTRC_NOCLKOUT
pragma target WDT    off
pragma target MCLRE  off
pragma target PWRTE  on
pragma target BODEN    on
pragma target LVP    off
-- HS, -BOD, ,-LVP, -WDT, -CP  = 0x3F22
; pragma target fuses       0x3D02
--enable_digital_io()

; const bit enhanced_flash_eeprom = false
include pic_general
include delay

var word result
var byte result_l
var byte result_h
var byte counter1
var byte counter2

const byte booted[] = "STARTED"

pin_a0_direction = input
pin_a1_direction = input
pin_a2_direction = input ; comparator in / vref out
pin_a3_direction = output ; comp1 out
pin_a4_direction = output ; comp2 out (OD)
pin_b2_direction=output ; RS232 TX

const bit usart_hw_serial = TRUE
const serial_hw_baudrate = 9600
include serial_hardware
serial_hw_init()

include print
include format

Procedure InitDeltaSigA2D() is
VRCON=0b11101100 --    0xEC
CMCON=0b00000110   -- 0x06 two common ref comps with outputs
end procedure

;
; Delta Sigma A2D
; The below contains a lot of nops and goto next instruction. These
; are necessary to ensure each pass through the Elop1 takes the same
; amount of , no the path through the code.
;
Procedure DeltaSigA2D() is
assembler
         LOCAL    elop1,comphigh,complow,eat2cycles,endelop1,eat5cycles,exit1
--DeltaSigA2D
        clrf counter1
        clrf counter2
        clrf result_l
        clrf result_h
        movlw 0x03 ; set up for 2 analog comparators with common reference
        movwf cmcon
elop1:
        btfsc c1out ; is comparator high or low?
--        btfsc cmcon,c1out ; is comparator high or low?
        goto complow ; go the low route
comphigh:
        nop ; necessary to timing even
        bcf porta,3 ; porta.3 = 0
        incfsz result_l,f ; bump counter
        goto eat2cycles ;
        incf result_h,f ;
        goto endelop1 ;
complow:
        bsf porta,3 ; comparator is low
        nop ; necessary to keep timing even
        goto eat2cycles ; same here
eat2cycles:
        goto endelop1 ; eat 2 more cycles
endelop1:
        incfsz counter1,f ; count this lap through the elop1.
        goto eat5cycles ;
        incf counter2,f ;
        movf counter2,w ;
        andlw 0x04 ; are we done? (we're done when bit2 of
--        btfsc status,_z ; the high order byte overflows to 1).
        btfsc Z --status,z ; the high order byte overflows to 1).
        goto elop1 ;
        goto exit1
eat5cycles:
        nop  -- this really seems to hate the goto $+1, keeps resetting. I am not so short of code space, case to be made to turn everything into nops
        nop
        --goto +1 ; more wasted time to keep the elop1s even
        nop ;
        goto elop1 ;
exit1:
        movlw 0x06 ; set up for 2 analog comparators with common reference
        movwf cmcon
end assembler
end procedure

function rescale(WORD IN value) RETURN WORD is
-- 1024 corresponds to 5V
-- multiply by 50000/1024 (~49)
var word temp =value*49
temp=temp/100 -- now lose excess precision
return temp

end function

InitDeltaSigA2D()

print_string(serial_hw_data,booted)

forever loop
DeltaSigA2D()
result=word(result_h)*256 + word(result_l) -- you must explicity make result_h a word as described on p11 of JAL manual
result=rescale(result)
format_word_dec(serial_hw_data, result, 3, 2)
serial_hw_data = " "
serial_hw_data = 13 -- CR
serial_hw_data = 10 -- LF
delay_1ms(500)   -- this is purely to allow the terminal display to catch up and not overload buffer of the slow device
end loop

Files

adc_test_16f628_v03.jal

and the hex file

adc_test_16f628_v03.hex

There’s a case to be made for using a voltage reference and setting it to be some convenient power of 2 like 2.05 V to save all the messy integer calculation in rescale

This is a video of the display on the serial port compared with a multimeter on the input, connected to a 5k pot between 0V and +5V. I should have moved the control a bit slower to allow the 0.5s sampling to catch up at times, but it shows it is reasonably accurate.