Nov 172018
 

Today, I decided to get cuddly with the relevant RFCs and see if I could adapt them into something that would work for AX.25.  The following roughly describes how one might stuff IPv6 datagrams into AX.25.

Much of this is heavily influenced by RFC-4944 and RFC-6282, the latter of which looks to be the heart-and-soul of Thread.


Stateless Automatic Addressing

We have a mechanism by which an AX.25 call+SSID can be losslessly mapped to a 48-bit MAC address. This is built on Radix-50 and can work as a stand-in for the EUI-48. The pseudo EUI-48 procedure mentioned in section 6 of the RFC-4944 standard is not required.

An EUI-64 is generated from an EUI-48 by chopping the EUI-48 in half and inserting the bytes ff:fe in the middle. So the EUI-48:

00:11:22:33:44:55

becomes the following EUI-64:

00:11:22:ff:fe:33:44:55

SLAAC therefore will work the same way it does for Ethernet.

Frame format

1. AX.25 UI Frame header

Size: (17 + (D*7) bytes, where D is the number of digipeaters being used

  • PID = 1100 0101 (tentative) IPv6
  • Control = 0000 0011
    • Frame type: UI, P/F = 0 (final)
  • Must contain source and destination AX.25 callsigns, may contain up to 8 digipeater AX.25 callsigns.

For a direct station-to-station contact:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
AX.25 Flag (0x7e) Destination AX.25 Call+SSID (7 bytes)
Source AX.25 Call+SSID (7 bytes)
AX.25 Control
AX.25 PID AX.25 UI payload here

or for contact via a few digipeaters:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
AX.25 Flag (0x7e) Destination AX.25 Call+SSID (7 bytes)
Source AX.25 Call+SSID (7 bytes)
Digi 1 Call+SSID
Digi 2 Call+SSID
AX.25 Control AX.25 PID AX.25 UI payload here

2. Mesh Addressing Header

To be used when two stations are not able to directly communicate, or when multicasting.

In this scenario, the AX.25 frame source and destination indicate the addresses of the directly-communicating nodes (e.g. source and digipeater, intermediate digipeaters, or digipeater and destination), and the fields given here will be the addresses of the source and destination AX.25 stations.

e.g. sending from VK4MSL-0 to VK4MDL-9 via
VK4RZB-0 and VK4RZA-0:

  1. First transmission:
    • AX.25 Src: VK4MSL-0
    • AX.25 Dst: VK4RZB-0
    • Mesh Src: VK4MSL-0
    • Mesh Dst: VK4MDL-9
    • Hops: 7
  2. Intermediate hop:
    • AX.25 Src: VK4RZB-0
    • AX.25 Dst: VK4RZA-0
    • Mesh Src: VK4MSL-0
    • Mesh Dst: VK4MDL-9
    • Hops: 6
  3. Final delivery:
    • AX.25 Src: VK4RZA-0
    • AX.25 Dst: VK4MDL-9
    • Mesh Src: VK4MSL-0
    • Mesh Dst: VK4MDL-9
    • Hops: 5

Unlike 802.15.4, we do not have 16-bit short addresses. Since these bits would otherwise always be set to 0, we will use these to provide a 6-bit “hops left” field. We shall use the value 63 (0x3f) to indicate when there are 63 or more hops remaining.

We will use the raw 48-bit addresses here. In keeping with amateur radio conventions, the source and destinations are flipped compared to RFC-4944.

Header format (13 bytes):

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
1 0 Hops Left Dest Address
Source Address
Remaining AX.25 UI payload

3. Broadcast header

The broadcast header is used with multicast messaging. In this case, the source and destination addresses in the  mesh addressing header MUST have the “group” bit set.

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 1 6LP_BC0 Sequence Number

4. Fragmentation header

To be used when a IPv6 datagram is greater than L bytes, where L may be defined to be between 64 and 216 bytes.

This part is identical to that of RFC-4944 (section 5.3).  I’ll come back to this bit.

5. IPv6 datagram

This can be encoded in a number of ways depending on requirements:

5.1. Raw IPv6 datagram

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
0 1 6LP_IPV6 IPv6 Datagram (with payload)

6LP_IPV6 is the value 0x01, as per RFC-4944. The IPv6 datagram is encoded as per RFC-2460, and includes its payload.

The AX.25 frame is finished off with the frame-check sequence.

5.2. Compressed IPv6 datagram

In this format, the datagram fields are compressed, either through making static assumptions, or by deriving them from things such as the AX.25 header, or a previously agreed-to context.

The first field in such payloads is the 6LP_IPHC field:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
0 1 1 6LP_IPHC with CID=1 Context ID Data follows

or without the context ID

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
0 1 1 6LP_IPHC with CID=0 Data follows

The 6LP_IPHC field is a 13-bit field, optionally followed by a context ID extension byte. The bit allocations are as follows:

0 1 2 3 4 5 6 7 8 9 10 11 12
TF NH HLIM CID SAC SAM M DAC DAM
  • (MSB) 0-1: TF Traffic Class, Flow Label. See 5.2.1 below.
  • 2: NH Next Header encoding
    • =0: Given explicitly
    • =1: Encoded using 6LP_NHC
  • 3-4: HLIM Hop Limit
    • =00: Given explicitly
    • =01: is set to 1
    • =10: is set to 64
    • =11: is set to 255
  • 5: CID Context Identifier Extension
    • =0: No CID byte follows
    • =1: A CID byte follows
  • 6-8: SAC Source Address Compression / SAM Mode
    • =000: No compression applied, whole address given
    • =001: Prefix is link-local prefix, remaining bits are given.
    • =x10: Not used in 6LoWHAM (we don’t support 16-bit addresses)
    • =011: Prefix is link-local, figure the rest out from the source address in the AX.25 header.
    • =100: Unspecified address ::
    • =101: See the context for the prefix, remaining bits are given.
    • =111: Figure out the address from the AX.25 header and context.
  • (LSB) 9-12: M Multicast, DAC Destination Address Compression
    DAM Mode

    • =0000: No compression, not multicast, whole address given
    • =0001: Prefix is link-local prefix, remaining bits are given. Not multicast.
    • =xx10: Not used in 6LoWHAM (we don’t support 16-bit addresses)
    • =0011: Prefix is link-local, figure the rest out from the destination address in the AX.25 header. Not multicast.
    • =0100: Reserved
    • =0101: See the context for the prefix, remaining bits are given. Not multicast.
    • =0111: Figure out the address from the AX.25 header and context. Not multicast.
    • =1000: No compression, multicast address, whole address given
    • =1001: 48-bits of multicast address given, fill in the blanks: ff__::00__:____:____.
    • =1010: 32-bits of multicast address given, fill in the blanks: ff__::00__:____.
    • =1011: 8-bits of multicast address given, fill in the blanks: ff02::00__.
    • =1100: 48-bits RFC-3306/RFC-3956 address, ff__:__LL:PPPP:PPPP:PPPP:PPPP:____:____ where P and L come from the context.
    • =1101: Reserved
    • =1110: Reserved
    • =1111: Reserved

The context ID extension byte has the following format:

0 1 2 3 4 5 6 7
SCI DCI
  • (MSB) 0-3: Source Context Identifier
  • (LSB) 4-7: Destination Context Identifier

These two sub-fields indicate which specific context is being used to fill in the blanks.

5.2.1: Traffic Class and Flow Label

These may be partially or completely omitted depending on the TF setting in the previous field.

  • TF=00:
    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
    ECN DCSP 0 0 0 0 Flow Label
  • TF=01:
    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
    ECN 0 0 Flow Label
  • TF=10:
    0 1 2 3 4 5 6 7
    ECN DCSP
  • TF=11: Flow label, ECN and DCSP are set to 0.

5.2.2: Next Header

If 6LP_NHC is not explicitly enabled, the next header byte will appear next.

5.2.3: Hop Limit.

Again, if not explicitly defined in the 6LP_IPHC header, the hop-limit byte will appear next.

5.2.4: Source address

The format here is determined by the values of SAC/SAM:

  • 000: Entire IPv6 address, 16 bytes given here.
  • x01: Last 8-bytes of the address given here
  • For all other values, the source address is omitted.

5.2.5: Destination address

The format here is determined by the values of M/DAC/DAM:

  • x000: Entire IPv6 address, 16 bytes given here.
  • 0x01: Last 8-bytes of the address given here.
  • 1001: 6-bytes of address given here, fill-in-the-blanks.
  • 1010: 4-bytes of address given here, fill-in-the-blanks.
  • 1011: Last byte of address given here, fill-in-the-blank.
  • 1100: 6-bytes of address given here, fill-in-the-blanks.
  • For all other values, the destination address is omitted.

6. 6LoWPAN Next Header

This is used to encode selected IPv6 extensions or L4 protocol headers.

6.1. IPv6 extension headers

A select number of IPv6 extensions may be encoded by replacing the usual “Next Header” byte with the following:

0 1 2 3 4 5 6 7
1 1 1 0 EID N

where EID (bits 4-6) is one of:

  • =0 IPv6 Hop-By-Hop options
  • =1 IPv6 Routing
  • =2 IPv6 Fragment
  • =3 IPv6 Destination Options
  • =4 IPv6 Mobility
  • =7 IPv6 Header

and N (bit 7) indicates whether the header’s payload is followed by another 6LowPAN Next Header, or a regular IPv6 Next Header (with its “Next Header” byte). For EID=7, N MUST be 0.

Length fields within the header payload should be counted in bytes instead of 8-byte blocks.

7. Datagram payload

7.1. Non-UDP payloads

For payloads other than UDP packets, these should be inserted into the AX.25 payload as-is following the extensions.

UDP packets with uncompressed headers should also be inserted
in this manner.

7.2. UDP payloads with header compression

For these payloads, the following UDP header should be used:

0 1 2 3 4 5 6 7 L-15 L-14 L-13 L-12 L-11 L-10 L-9 L-8 L-7 L-6 L-5 L-4 L-3 L-2 L-1 L
1 1 1 1 0 C P Source Address Destination Address Checksum (unless C=1)
  • (MSB): bits 0-4: Compressed UDP header marker. Literal 11110₂
  • Bit 5: C Compressed UDP checksum
    • 0= UDP checksum is given (recommended value)
    • 1= UDP checksum is omitted
  • Bits 6-7: P Ports
    • 00=Both source and destination addresses are
      given in full

      0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
      Source Port (16-bits) Destination Port (16-bits)
    • 01=Source port is given in full, Least significant 8-bits of destination given, destination port is 0xff00-0xffff (65280-65535)
      0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
      Source Port (16-bits) Destination Port (8-bits)
    • 10=Destination port is given in full, Least significant 8-bits of source given, source port is 0xff00-0xffff (65280-65535)
      0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
      Source Port (8-bits) Destination Port (16-bits)
    • 11=Only least significant 4-bits of source and destination ports are given. Port LSB range is 0xf0b0-0xf0bf (61616-61631)
      0 1 2 3 4 5 6 7
      Source Port (4-bits) Destination Port (4-bits)

The C bit should only be set if the upper-level application asks for it. Whilst 802.15.4 does its own CRC as does AX.25, the field is mandatory in UDP and the recommendation is to only drop it if the application says it’s okay.

Nov 162018
 

I’ve been doing more pondering on the routing side of things.  The initial thought was to use Net/ROM L3 to figure out the source route of who can hear whom.  Getting access to that via BPQ’s interfaces may not be easy unless we happen to eavesdrop on the broadcasts, and of course, there’s no service discovery in BPQ.

The thought came to me, what does ICMPv6 offer me in terms of routing?  If I can ask “who has a route to node X?” or announce “I know how to reach X”… I could just skip Net/ROM L3 altogether, since there’s a good chance both will co-exist quite happily on the same AX.25 network, we just take a ship-pass-in-the-night approach.

ICMPv6 Router Advertisements basically just say “I’m a router and this is my local prefix”, similarly, ICMPv6 Router Discovery messages just ask “Who’s a router?” … not greatly helpful.

RFC-4191 gets close, specifically the Route Information Option, but this is again, targeted at reaching a node on a differing subnet to the local one, and only applies to RAs.

This is a service that 802.15.4 actually provides to 6LoWPAN (RFC-4944), so from the point of view of IPv6, a 802.15.4 network translated through 6LoWPAN “looks” like one L2 network, it just needs to know a node’s extended address which is what made me think about Net/ROM L3 in the first place.

RFC-6775 looks to enhance things a little bit, by considering the fact that it’s not just one big happy family however, not everyone can talk to each-other, and links come and go.

One thing is clear, not everybody will be a router.  Specifically, a node should definitely not advertise itself as a router unless it can hear at least two other nodes, or knows routes to nodes learned either through static configuration or via Net/ROM node advertisements.

Two nodes can just exclusively use link-layer communications, so there’s no need for either to be “routers”.  Soon as a third joins in, potentially all three could be routers if they can all hear each-other, but if you have a linear topology where only the central node can hear the other two, it is logical that that node becomes a router, and not the others.

The question is then, if one of those peripheral nodes disappears, what should the router do?  I’m thinking it should remain a router for a limited period of time (configurable, but maybe measured in hours), just in case that node returns or other nodes appear.  After some time, it may “demote” itself to non-routing node status and relinquish control of the on-mesh prefix.

Where a node promotes itself to router, if an existing on-mesh prefix is in use, it should continue to use that, otherwise it should derive a suitable ULA prefix for use.

It may also follow that a ULA is configured for certain nodes, and they are configured to remain in the router role, regardless of the number of neighbours.  Repeater sites would be prime candidates for this.  They’re in a position where they should have good coverage, and thus should be prime candidates to be routers.

Since we can do multi-hop source routing with AX.25, there’s scope to perhaps exploit this in a higher level protocol which we might build on UDP messaging, as it doesn’t look like the existing standards provide for this sort of route path all that well.  TCP/IP is after all destination routed whilst AX.25 is source routed.

I think maybe tomorrow (which is predicted to be wet), it’ll be a good day to sit down and prototype something that maybe takes care of the IP messaging side of things and at least gets two AX.25 stations exchanging messages, then we can start to build something atop that.

Nov 152018
 

Having discussed the idea with a few people, both on the linux-hams mailing list and off-list, I’m starting to formalise a few plans for how this might work.

One option is to augment existing software stacks and inter-operate not just over-the-air, but at an API level.  Brisbane WICEN have a fleet of TNCs all running TheNet X1J, which was a popular Net/ROM software stack for TAPR TNC2-compatible TNCs in the early 90s.  Slowly, these are being replaced with Raspberry Pis equipped with Pi-TNCs and running LinBPQ.

These two inter-operate quite well, and the plan looks to be, to slowly upgrade all the sites to LinBPQ nodes.

Now, 6LoWHAM on TNCs that are nearly as old as I am just isn’t going to fly, but if I can link up to LinBPQ, this alternate protocol can be packaged up and installed along-side LinBPQ in an unobtrusive manner.

There are two things I need to be able to do:

  • Send and receive raw AX.25 frames
  • Read the routing table from LinBPQ

Sending and receiving raw frames

Looking at the interfaces that LinBPQ (and BPQ32) offers, the most promising option looks to be the AGWPE-compatible interface.  The protocol is essentially a TCP link over which the AX.25 frames are encapsulated and sent.

There’s a good description of the protocol here, and looking at the sources for LinBPQ (third link from the bottom of the page), it looks as if the necessary bits of the protocol are present to send and receive raw frames.

In particular, to send raw UI frames, I need to send these as ‘M’ (direct) or ‘V’ frames (via digipeater), and to receive them, I need to make use of the monitoring mode (‘m’ frame).

Reading the routing table

This, is where things will be “fun”.  The AGWPE interface does offer a “heard” frame, which can report on what stations have been heard.  This I think isn’t going to be the holy grail I’m after, although it’ll be a start, maybe.

Alternatively, a way around this might be to “eavesdrop” on the Net/ROM routing frames.  In monitor mode, I should theoretically hear all traffic, including these Net/ROM beacons.  It’s not as nice as being able to simply read LinBPQ’s routing table, but at least I don’t have to generate the Net/ROM messages.

The other way would be to connect to the terminal interface on LinBPQ, and use the NODES command, parsing that.  Ugly, but it’ll get me by.  On that same page is NRR… which looks to be similar in function to TCP/IP’s traceroute.  The feature is also supported by JNOS 2.0, which was released in 2006.  Not old by packet radio standards, but old enough.

Identifying if a remote station supports 6LoWHAM

Now, this is the tricky bit.  Identifying an immediate neighbour is easy enough, you can simply send an ICMPv6 neighbour solicitation message and see if they respond.  In fact, I’m thinking that could be the immediate first step.  There’s no support for service discovery as such, but nodes could advertise an “alias” (just one).

The best bet may be a suck-it-and-see approach.  We should be able to “digipeat” via intermediate nodes as if they were plain L2 AX.25 digipeaters, thus if we have a reason to contact a given node (i.e. there’s unicast traffic queued up to be sent there), we can just try routing an AX.25 frame with a ICMPv6 neighbour solicitation and see if we get a neighbour advertisement.

This carries a risk though: a station may not react well to unknown traffic and may try to parse the message as something it is not.  Thus for unicast, it is not a fail-safe method.

Multicast traffic however will be a challenge, and much of IPv6 relies on multicast.  The Net/ROM station will not know anything about this, as it simply wasn’t a concept back in the day.

For subnets like ff03::1, which on Thread networks usually means “all full-function Thread devices”, this could be sent via non-6LoWHAM digipeaters by broadcasting via that digipeater to the AX.25 station alias “6LHMC” (6LoWHAM Multicast).

This could be used to provide tunnelling of multicast traffic where a route to a station has been discovered via Net/ROM and we need to safely test whether the station can in fact understand 6LoWHAM traffic without the risk of crashing it.

I think the next step might be to look at how a normal IPv6 node would “register” interest in a multicast group so that routers between it and the sender of such a group know where to forward traffic.  IPv6 does have such a mechanism, and I think understanding how multicast traverses subnets is going to be key to making this work.

Nov 102018
 

Right now, the cluster is running happily with a Redarc BCDC-1225 solar controller, a Meanwell HEP-600C-12 acting as back-up supply, a small custom-made ATTiny24A-based power controller which manages the Meanwell charger.

The earlier purchased controller, a Powertech MP-3735 now is relegated to the function of over-discharge protection relay.  The device is many times the physical size of a VSR, and isn’t a particularly attractive device for that purpose.  I had tried it recently as a solar controller, but it’s fair to say, it’s rubbish at it.  On a good day, it struggles to keep the battery above “rock bottom” and by about 2PM, I’ll have Grafana pestering me about the battery slipping below the 12V minimum voltage threshold.

Actually, I’d dearly love t rip that Powertech controller apart and see what makes it tick (or not in this case).  It’d be an interesting study in what they did wrong to give such terrible results.

So, if I pull that out, the question is, what will prevent an over-discharge event from taking place?  First, I wish to set some criteria, namely:

  1. it must be able to sustain a continuous load of 30A
  2. it should not induce back-EMF into either the upstream supply or the downstream load when activated or activated
  3. it must disconnect before the battery reaches 10.5V (ideally it should cut off somewhere around 11-11.5V)
  4. it must not draw excessive power whilst in operation at the full load

With that in mind, I started looking at options.  One of the first places I looked was of course, Redarc.  They do have a VSR product, the VS12 which has a small relay in it, rated for 10A, so fails on (1).  I asked on their forums though, and it was suggested that for this task, a contactor, the SBI12, be used to do the actual load shedding.

Now, deep inside the heart of the SBI12 is a big electromechanical contactor.  Many moons ago, working on an electric harvester platform out at Laidley for Mulgowie Farming Company, I recall we were using these to switch the 48V supply to the traction motors in the harvester platform.  The contactors there could switch 400A and the coils were driven from a 12V 7Ah battery, which in the initial phases, were connected using spade lugs.

One day I was a little slow getting the spade lug on, so I was making-breaking-making-breaking contact.  *WHACK*… the contactor told me in no uncertain terms it was not happy with my hesitation and hit me with a nice big back-EMF spike!  I had a tingling arm for about 10 minutes.  Who knows how high that spike was… but it probably is higher than the 20V absolute maximum rating of the MIC29712s used for power regulation.  In fact, there’s a real risk they’ll happily let such a rapidly rising spike straight through to the motherboards, frying about $12000 worth of computers in the process!

Hence why I’m keen to avoid a high back-EMF.  Supposedly the SBI12 “neutralises” this … not sure how, maybe there’s a flywheel diode or MOV in there (like this), or maybe instead of just removing power in a step function, they ramp the current down over a few seconds so that the back-EMF is reduced.  So this isn’t an issue for the SBI12, but may be for other electromechanical contactors.

The other concern is the power consumption needed to keep such a beast activated.  The other factor was how much power these things need to stay actuated.  There’s an initial spike as the magnetic field ramps up and starts drawing the armature of the contactor closed, then it can drop down once contact has been made.  The figures on the SBI12 are ~600mA initially, then ~160mA when holding… give or take a bit.

I don’t expect this to be turned on frequently… my nodes currently have up-times around 172 days.  So while 600mA (7~8W at 12V nominal) is high, that’ll only be for a second at most.  Much of the current will be holding current at, let’s call it 200mA to be safe, so about 2~3W.

That 2-3W is going to be the same, whether my nodes collectively draw 10mA, 10A or 100A.

It seemed like a lot, but then I thought, what about a SSR?  You can buy a 100A DC SSR like this for a lot less money than the big contactors.  Whack a nice big heat-sink on it, and you’re set.  Well, why the heat-sink?  These things have a voltage drop and on resistance.  In the case of the Jaycar one, it’s about 350mV and the on resistance is about 7mΩ.

Suppose we were running flat chat at our predicted 30A maximum…

  • MOSFET switch voltage drop: 30A × 350mV = 10.5W
  • Ron resistance voltage drop: (30A)² × 7mΩ = 6.3W
  • Total power dissipation: 10.5W + 6.3W = 16.8W OUCH!

16.8W is basically the power of an idle compute node.  The 3W of the SBI12 isn’t looking so bad now!  But can we do better?

The function of a solid-state relay, amongst other things, is to provide electrical isolation between the control and switching components.  The two are usually galvanically isolated.  This is a feature I really don’t need, so I could reduce costs by just using a bare MOSFET.

The earlier issues I had with the body diode won’t be a problem here as there’s a definite “source” and “load”, there’ll be no current to flow out of the load back to the source to confuse some sensing circuit on the source side.  This same body diode might be an issue for dual-battery systems, as the auxiliary battery can effectively supply current to a starter motor via this body diode, but in my case, it’s strictly switching a load.

I also don’t have inductive loads on my system, so a P-channel MOSFET is an option.  One candidate for this is the Infineon AUIRFS3004-7P.  The Ron on these is supposedly in the realm of 900µΩ-1.25mΩ, and of course, being that it’s a bare MOSFET and not a SSR, there’s no voltage drop.  Thus my power dissipation at 30A is predicted to be a little over 1W.

There are others too with even smaller Ron values, but they are in teeny tiny 5mm square surface-mount packages.  The AUIRFS3004-7P looks dead-buggable, just bend up the gate pin so I can solder direct to it, and treat the others as single “pins”, then strap the sucker to a big heatsink (maybe an old PIII heatsink will do the trick).

I can either drive this MOSFET with something of my own creation, or with the aforementioned Redarc VS12.  The VS12 still does contain a (much smaller) electromechanical relay, but at 30mA (~400mW), it’s bugger all.

The question though was what else could be done?  @WIRING_SOLUTIONS suggested some units made by Victron Energy.  These do have a nice feature in that they also have over-voltage protection, and conveniently, it’s 16V, which is the maximum recommended for the MIC29712s I’m using.  They’re not badly priced, and are solid-state.

However, what’s the Ron, what’s the voltage drop?  Victron don’t know.  They tell me it’s “minimal”, but is that 100nV, 100mV, 1V?  At 30A, 100mV drop equates to 3W, on par with the SBI12.  A 500mV drop would equate to a whopping 15W!

I had a look at the suppliers for Victron Energy products, and via those, found a few other contenders such as this one by Baintech and the Projecta LVD30.  I haven’t asked about these, but again, like the Victron BatteryProtect, neither of these list a voltage drop or Ron.

There’s also this one from Jaycar, but given this is the same place that sold me the Powertech MP-3735, and sold me the original Powertech MP-3089, provided a replacement for that first one, then also replaced the replacement under RMA.  The Jaycar VSR also has practically no specs… yeah, I think I’ll pass!

Whitworths marine sell this, it might be worth looking at but the cut-out voltage is a little high, and they don’t actually give the holding current (330mA “engage” current sounds like it’s electromechanical), so no idea how much power this would dissipate either.

The power controller isn’t doing a job dissimilar to a VSR… in fact it could be repurposed as one, although I note its voltage readings seem to drift quite a lot.  I suspect this is due to the choice of 5% tolerance resistors on the voltage sensing circuit and my use of the ~1.1V internal voltage reference.  The resistors will drift a little bit, and the voltage reference can be anywhere from 1.0 to 1.2V.

Would a LM311N with good quality 1% resistors and a quality voltage reference be “better”?  Who knows?  Maybe I should try an experiment, see if I can get minimal drift out of a LM311N.  It’s either the resistors, the voltage reference, or a combination of the two that’s responsible for the power controller’s drift.

Perhaps I need to investigate which is causing the problem and see what can be done in the design to reduce it.  If I can get acceptable results, then maybe the VS12 can be dispensed with.  I may be able to do it with another ATTiny24A, or even just a simple LM311N.

Oct 272018
 

So earlier, I had mentioned that it’s really not desirable to have ARQ (automatic repeat request) on a link carrying TCP datagrams.  My comment is based on this observation:

http://sites.inka.de/bigred/devel/tcp-tcp.html

In that article, the discussion is about one TCP connection being tunnelled over another TCP connection.  Basically it comes down to the lower layer buffering and re-sending the TCP datagrams just as the upper layer gives up on hearing a reply and re-sends its own attempt.

Now, end-to-end ACKs have been done on long chains of AX.25 networks before.  It’s generally accepted to be an unreliable mechanism.  UDP for sure can benefit, but then many protocols that use UDP already do their own handling of lost messages.  CoAP for instance does its own ARQ, as does TFTP.

Gerald Wagenknecht, Markus Anwander and Torsten Braun discuss some of the impacts of this on a 802.15.4 network in their thesis “Hop-to-Hop Reliability in IP-based Wireless Sensor Networks – a Cross-Layer Approach“.  In this, they talk about a variant of TCP called TSS: TCP Support for Sensor Networks.  This was discussed at depth in a thesis by Adam Dunkels, “Towards TCP/IP for Wireless Sensor Networks“.

This latter document, was apparently the inspiration for 6LoWPAN.  Section 4.4.3 discusses the approaches to handling ARQ in TCP.  Section 9.6 goes into further detail on how ARQ might be handled elsewhere in the network.

Thankfully in our case, it’s only the network that’s constrained, the nodes themselves will be no smaller than a Raspberry Pi which would have held its own against the PC that Adam Dunkels used to write that thesis!

In short, it looks as if just routing IP packets is not going to cut it, we need to actually handle the TCP side of things as well.  As for other protocols like CoAP, I guess the answer is be patient.  The timeout settings defined in RFC-7252 are usually tuneable, and it may be desirable to back those off just a little for use over AX.25.

Oct 272018
 

So, for the past few weeks I’ve been running a Redarc BCDC-1225 solar controller to keep the batteries charged.  I initially found I had to make my little power controller back off on the mains charger a bit, but was finally able to prove conclusively that the Redarc was able to operate in both boost and float modes.

In the interests of science, I have plugged the Powertech back in.  I have changed nothing else.  What I’m interested to see, is if the Powertech in fact behaves itself, or whether it will go back to its usual tricks.

The following is the last 6 hours.

Next week, particularly Thursday and Friday, are predicted to have similar weather patterns to today. Today’s not a good test, since the battery started at a much higher voltage, so I expect that the solar controller will be doing little more than keeping the battery voltage up to the float set-point.

For reference, the settings on the MP-3735 are: Boost voltage 14.6V, Float voltage 13.8V. These are the recommended settings according to Century’s datasheets for the batteries concerned.

Interestingly, no sooner do I wire this up, but the power controller reaches for the mains. The MP-3735 definitely likes to flip-flop. Here’s a video of its behaviour shortly after connecting up the solar (and after I turned off the mains charger at the wall).

Now looking, it’s producing about 10A, much better than the 2A it was doing whilst filming.  So it can charge properly, when it wants to, but it’s intermittent, and inside you can sometimes hear a quiet clicking noise as if it’s switching a relay.  At 2A it’s wasting time, as the cluster draws nearly 5× that.

The hesitation was so bad, the power controller kicked the mains charger in for about 30 minutes, after that, the MP-3735 seems to be behaving itself.  I guess the answer is, see what it does tomorrow, and later this week without me intervening.

If it behaves itself, I’m happy to leave it there, otherwise I’ll be ordering a VSR, pulling out the Powertech MP-3735 and re-instating the Redarc BCDC-1225 with the VSR to protect against over-discharge.


Update 2018-10-28… okay, overcast for a few hours this morning, but by 11AM it had fined up.  The solar performance however was abysmal.

Let’s see how it goes this week… but I think I might be ordering that VSR and installing the Redarc permanently now.


Today’s effort:

Each one of those vertical lines was accompanied by a warning email.

Oct 212018
 

So, I placed an order to Mouser the other day to actually get some parts into my hands so I can better design the boards.

In that, I discovered the screw terminals I was planning on using, are discontinued.  So, I found something that was able to take the same gauge wire: Phoenix 1017526s.  Turns out, these will not fit along side the current shunts on the board as planned.

There’s just no way I’ll be fitting these on a 5×5cm board and have room to spare for a shunt in between.  Since this is really application-specific, it might be better off board.  We’ll put the INA219 and PCA9615 together on the board so we have a nice self-contained sensor board that can be mounted close to the current shunt, wherever that lives, and have nice noise-resistant links back to the controller.

This does mean I can do things like put a current shunt in the fuse box where the solar panels connect, and run CAT5 down to the controller from there.

To make routing easier, I’ve gone to a 4-layer board.  The board has solder-jumpers for setting the I²C address of the INA219, and I’ve documented all the termination and pull resistors.  I’m not sure what ones are needed yet, so there’s space at every point where I could envisage one being needed.

There’s two power planes in the inner layers, one for VCC the other for 0V.

Next step, I’ll print out the board designs and test fit everything before ordering the boards, which I hope to have ordered this afternoon.

Oct 202018
 

So, doing some more digging here.  One question people might ask is what kind of applications would I use over this network?

Bear in mind that it’s running at 1200 baud!  If we use HTTP at all, tiny is the word!  No bloated images, and definitely no big heavy JavaScript frameworks like ReactJS, Angular, DoJo or JQuery.  You can forget watching Netflicks in 4k over this link.

HTTP really isn’t designed for low-bandwidth links, as Steve Netting demonstrated:

The page itself is bad enough, but even then, it’s loaded after a minute.  The real slow bit is the 20kB GIF.

So yeah, slow-scan television, the ability to send weather radar images over, that is something I was thinking of, but not like that!

HTTP uses pretty verbose headers:

GET /qld/forecasts/brisbane.shtml?ref=hdr HTTP/1.1
Host: www.bom.gov.au
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-AU,en-GB;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://www.bom.gov.au/products/IDR664.loop.shtml
Cookie: bom_meteye_windspeed_units_knots=yes
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Pragma: no-cache
Cache-Control: no-cache

HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Encoding: gzip
Content-Type: text/html; charset=UTF-8
Server: Apache
Vary: Accept-Encoding
Content-Length: 6321
Date: Sat, 20 Oct 2018 10:56:12 GMT
Connection: keep-alive

That request is 508 bytes and the response headers are 216 bytes.  It’d be inappropriate on 6LoWPAN as you’d be fragmenting that packet left right and centre in order to squeeze it into the 128-byte 802.15.4 frames.

In that video, ICMP echo requests were also demonstrated, and those weren’t bad!  Yes, a little slow, but workable.  So to me, it’s not the packet network that’s the problem, it’s just that something big like HTTP is just not appropriate for a 1200-baud radio link.

It might work on 9600 baud packet … maybe.  My Kantronics KPC3 doesn’t do 9600 baud over the air.

CoAP was designed for tight messages.  It is UDP based, so your TCP connection overhead disappears, and the “options” are encoded as individual bytes in many cases.  There are other UDP-based protocols that would work fine too, as well as older TCP protocols such as Telnet.

A request, and reply in CoAP look something like this:

Hex dump of request:
00000000  40 01 00 01 3b 65 78 61  6d 70 6c 65 2e 63 6f 6d   @...;exa mple.com
00000010  81 63 03 52 46 77 11 3c                            .c.RFw.< 

Hex dump of response:
    00000000  60 45 00 01 c1 3c ff a1  1a 00 01 11 70 a1 01 a3   `E...<.. ....p...
    00000010  04 18 64 02 6b 31 39 32  2e 31 36 38 2e 30 2e 31   ..d.k192 .168.0.1
    00000020  03 64 65 74 68 30                                  .deth0

Or in more human readable form:

Request:
Constrained Application Protocol, Confirmable, GET, MID:1
    01.. .... = Version: 1
    ..00 .... = Type: Confirmable (0)
    .... 0000 = Token Length: 0
    Code: GET (1)
    Message ID: 1
    Opt Name: #1: Uri-Host: example.com
        Opt Desc: Type 3, Critical, Unsafe
        0011 .... = Opt Delta: 3
        .... 1011 = Opt Length: 11
        Uri-Host: example.com
    Opt Name: #2: Uri-Path: c
        Opt Desc: Type 11, Critical, Unsafe
        1000 .... = Opt Delta: 8
        .... 0001 = Opt Length: 1
        Uri-Path: c
    Opt Name: #3: Uri-Path: RFw
        Opt Desc: Type 11, Critical, Unsafe
        0000 .... = Opt Delta: 0
        .... 0011 = Opt Length: 3
        Uri-Path: RFw
    Opt Name: #4: Content-Format: application/cbor
        Opt Desc: Type 12, Elective, Safe
        0001 .... = Opt Delta: 1
        .... 0001 = Opt Length: 1
        Content-type: application/cbor
    [Uri-Path: coap://example.com/c/RFw]

Response:
Constrained Application Protocol, Acknowledgement, 2.05 Content, MID:1
    01.. .... = Version: 1
    ..10 .... = Type: Acknowledgement (2)
    .... 0000 = Token Length: 0
    Code: 2.05 Content (69)
    Message ID: 1
    Opt Name: #1: Content-Format: application/cbor
        Opt Desc: Type 12, Elective, Safe
        1100 .... = Opt Delta: 12
        .... 0001 = Opt Length: 1
        Content-type: application/cbor
    End of options marker: 255
    Payload: Payload Content-Format: application/cbor, Length: 31
        Payload Desc: application/cbor
        [Payload Length: 31]
Concise Binary Object Representation
    Map: (1 entries)
        Unsigned Integer: 70000
            Map: (1 entries)
                ...0 0001 = Unsigned Integer: 1
                    Map: (3 entries)
                        ...0 0100 = Unsigned Integer: 4
                            Unsigned Integer: 100
                        ...0 0010 = Unsigned Integer: 2
                            Text String: 192.168.0.1
                        ...0 0011 = Unsigned Integer: 3
                            Text String: eth0

That there, also shows another tool to data packing: CBOR.  CBOR is basically binary JSON.  Just like JSON it is schemaless, it has objects, arrays, strings, booleans, nulls and numbers (CBOR differentiates between integers of various sizes and floats).  Unlike JSON, it is tight.  The CBOR blob in this response would look like this as JSON (in the most compact representation possible):

{70000:{4:100,2:"192.168.0.1",3:"eth0"}}

The entire exchange is 190 bytes, less than a quarter of the size of just the HTTP request alone.  I think that would work just fine over 1200 baud packet.  As a bonus, you can also multicast, try doing that with HTTP.

So you’d be writing higher-level services that would use this instead of JSON-REST interfaces.  There’s a growing number of libraries that can consume this sort of thing, and IoT is pushing that further.  I think it’s doable.

Now, on the routing front, I’ve been digging up a bit on Net/ROM.  Net/ROM is actually two parts, Net/ROM Level 3 does the routing and level 4 does the circuit switching.  It’s the “Level 3” bit we want.

Coming up with a definitive specification of the protocol has been a bit tough, it doesn’t help that there is a company called NetROM, but I did manage to find this document.  In a way, if I could make my software behave like a Net/ROM node, I could piggy-back off that to discover neighbours.  Thus this protocol would co-exist along side Net/ROM networks that may be completely oblivious to TCP/IP.

This is preferable to just re-inventing the wheel…yes I know non-circular wheels are so much fun!  Really, once Net/ROM L3 has figured out where everyone is, IP routing just becomes a matter of correctly addressing the AX.25 frame so the next hop receives the message.

VK4RZB at Mt. Coot-tha is one such node running TheNet.  Easy enough to do tests on as it’s a mere stone throw away from my home QTH.

There’s a little consideration to make about how to label the AX.25 frame.  Obviously, it’ll be a UI frame, but what PID field should I use?  My instinct suggests that I should just label it as “ARPA Internet Protocol”, since it is Internet Protocol traffic, just IPv6 instead of v4.  Not all the codes are taken though, 0xc9 is free, so I could be cheeky and use that instead.  If the idea takes off, we can talk with the TAPR then.

Oct 102018
 

This is another brain dump of ideas.

So, part of me wants to consider the idea of using amateur radio as a transmission mechanism for 6LoWPAN.  The idea being that we use NET/ROM and AX.25 or similar schemes as a transport mechanism for delivering shortened IPv6 packets.  Over this, we can use standard TCP/IP programming to write applications.

Protocols designed for low-bandwidth constrained networks are ideal here, so things like CoAP where emphasis is placed on compact representation.  6LoWPAN normally runs over IEEE 802.15.4 which has a payload limit of 128 bytes.  AX.25 has a limit of 256 bytes, so is already doing better.

The thinking is that I “encode” the call-sign into a “hardware” address.  MAC addresses are nominally 48-bits, although the IEEE is trying to phase that out in favour of 64-bit EUIs.  Officially the IEEE looks after this, so we want to avoid doing things that might clash with their system.

A EUI-48 (MAC) address is 6-bytes long, where the first 3 bytes identify the type of address and the organisation, and the latter 3 bytes identify an individual device.  The least significant two bits of the first byte are flags that decide whether the address is unicast or local, and whether it is globally administered (by the IEEE) or locally administered.

To avoid complications, we should probably keep the unicast bit cleared to indicate that these addresses are unicast addresses.

Some might argue that the ITU assigns prefixes to countries, and these countries have national bodies that hand out callsigns, thus we could consider callsigns as “globally administered”.  Truth is, the IEEE has nothing to do with the process, and could very legitimately assign the EUI-48 prefix 56-4b-34 to a company… in that hypothetical scenario, there goes all the addresses that might represent amateur operators stationed in Queensland.  So let’s call these “locally administered”, since there are suffixes the user may choose (e.g. “/P”).

That gives us 46-bits to play with.  7-bit ASCII just fits 6 characters, which would just fit the callsigns used in AX.25 with enough room for a 4-bit SSID.  We don’t need all 128 characters though, and a scheme based on DEC’s Radix50 can pack in far more.

We can get 8 arbitrary Radix50 characters into 43 bits, which gives us 3 left over which can be used as the user wishes.  We’ll probably call it the SSID, but unlike AX.25, will be limited from 0-7.  The user can always use the least significant character in their callsign field for an additional 6 bits, which gives them 9 bits to play with.  (i.e. “VK4MSL-1″#0 to encode the AX.25 SSID “VK4MSL-10”)

Flip the multicast bit, and we’ve got a group address.

SLAAC derives the IPv6 address from the EUI-48, so the IPv6 address will effectively encode the callsigns of the two communicating stations.  If both are on the same “mesh”, then we can probably borrow ideas from 6LoWPAN for shortening that address.

Oct 062018
 

So, I’ve designed the sensor board, this is basically a break-out of an INA219 coupled with a PCA9615, for extended I²C range.  If I was to use one of these on the cluster, it’s theoretically possible for me to put one up in the fuse box on the back deck, and run CAT5e down to the server rack to help measure voltage drop across that long run.  Doing that with regular I²C would be insane.

Again, I’ve gone crazy with pull-up, pull-down and termination resistances, not knowing what would be needed.  The schematic is nothing special.

The board wound up bigger than I’d expected, but largely because it had to accommodate fairly heavy power traces.  I think I’ve got the footprint for the screw terminal blocks right.  I’ve managed to cram it onto a 5cm×5cm board (two layer).

As always, you’ve got two ways of dealing with the current shunt, either hook one up externally, which means you don’t bother with the beefy power connection footprints, or you fit a surface-mount shunt on.

You’ve got full flexibility there, as well as what address to set the board to via the jumpers.

I’ll probably order some of the connectors and other parts in question, print out the board layout and test-fit everything.  I’m not happy about the fact that NXP only make the PCA9615 in TSSOP, but I guess I should be thankful the part has legs.