Networking

How to access LAN9303 registers over I2C using Arduino / PlatformIO

The LAN9303 has some peculiarities when accessing its registers. This post will not cover indirect register access but only access to the registers which are directly accessible over I2C. Note that the prerequisite for this is to configure the LAN9303 into a mode where management over the I2C slave interface is enabled. See How to get the LAN9303 into I2C managed mode for more info on how to do that.

The main point to take away is that you do not write the register offset from the datasheet (such as 0x50 for the Chip ID and revision register) in the I2C address byte but the address divided by 4 (0x50 >> 2 == 0x14). This is evidenced by figure 8-8 from the datasheet, copyright Microchip, listing the address byte as A[9:2] as opposed to the standard A[7:0]:

 

Example on how to access the register at offset 0x50 (Chip ID and revision register) in Arduino using the Wire library:

const uint8_t LAN9303_I2C_ADDRESS = 0b1010;
const uint16_t LAN9303_CHIPID_REV_Register = 0x50;

Wire.beginTransmission(LAN9303_I2C_ADDRESS);
Wire.write(LAN9303_CHIPID_REV_Register >> 2);
Wire.endTransmission();
Wire.requestFrom(LAN9303_I2C_ADDRESS, 4); // This register is 32 bits = 4 bytes long
delay(5); // Wait for data to be available

// Read directly into an uint8_t
uint32_t buf;
size_t actually_read = Wire.readBytes((uint8_t*)&buf, 4);
// Check if we have received all 4 bytes
if(actually_read != 4) {
    Serial.println("Did not read enough bytes");
}

// Print register value
Serial.printf("LAN9303 Chip ID and revision: %08lx\r\n", __builtin_bswap32(buf));

This will print

LAN9303 Chip ID and revision: 93030001

in other words: Chip ID = 0x9303, revision = 0x0001

 

Posted by Uli Köhler in Arduino, C/C++, Electronics, Embedded, Networking, PlatformIO

What is the LAN9303 I2C slave address?

I experimentally determined that the LAN9303 uses the I2C slave address 0b0001010. This is listed in the datasheet in section 8.5.1.

Posted by Uli Köhler in Electronics, Embedded, Networking

How to fix Caddy container generating docker volume with autosave.json

My docker-compose-based Caddy setup re-created the container and hence created a new docker volume with only the autosave.json whenever it was restarted. Since it was auto-restarted once a minute, this led do over 70000 volumes piling up in /var/lib/docker/volumes.

The Caddy log shows that Caddy is creating /config/caddy/autosave.json:

mycaddy_1 | {"level":"info","ts":1637877640.7375677,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}

I fixed this by mapping /config/caddy to a local directory:

- ./caddy_data:/config/caddy/

Complete docker-compose.yml example:

version: '3.5'
services:
  mycaddy:
    image: 'caddy:2.4.6-alpine'
    volumes:
      - ./caddy_data:/config/caddy/
      - ./static:/usr/share/caddy
      - ./Caddyfile:/etc/caddy/Caddyfile
    ports:
      - 19815:80

 

Posted by Uli Köhler in Docker, Networking

How to fix mosquitto local-only mode despite ‘listener 1883’

If mosquitto is printing the local only message even though you have listener 1883 in your config file, check if mosquitto is using the correct config file. In my case, I mis-spelled the config file path (conf instead of config), hence mosquitto used the default config file, not my config file and therefore ignored all statements I put in my config file.

Posted by Uli Köhler in MQTT, Networking

How to fix Mosquitto ‘exited with code 13’

When mosquitto exits with code 13 such as a in a docker based setup, you will often see not error messsage:

Attaching to mosquitto_mosquitto_1
mosquitto_mosquitto_1 exited with code 13

However, there will be an error message in mosquitto.logSo, ensure that you have configured a log_dest file in your mosquitto.conf such as:

log_dest file /mosquitto/log/mosquitto.log

and check that file. In my case it showed these error messages:

1637860284: mosquitto version 2.0.14 starting
1637860284: Config loaded from /mosquitto/config/mosquitto.conf.
1637860284: Error: Unable to open pwfile "/mosquitto/conf/mosquitto.passwd".
1637860284: Error opening password file "/mosquitto/conf/mosquitto.passwd".

In my case, the path of the password file was mis-spelled (conf instead of config)

Note that you need to create the password file in order for mosquitto to start up!

See How to setup standalone mosquitto MQTT broker using docker-compose for example commands on how to create the user and the password file

 

Posted by Uli Köhler in Docker, MQTT

How to disable mosquitto MQTT local-only mode and listen on all IP addresses

When starting Mosquitto using a default configuration file, you will see log message like

mosquitto_1  | 1637858580: Starting in local only mode. Connections will only be possible from clients running on this machine.

indicating that the mosquitto MQTT broker is only listening on 127.0.0.1 and is not reachable over the network.

In order to fix this, you can simply bind to all IP addresses using

bind_address 0.0.0.0
listener 1883

in mosquitto.conf

Full mosquitto.conf example

persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log

listener 1883
## Authentication ##
allow_anonymous false
password_file /mosquitto/conf/mosquitto.conf

See our previous post on How to setup standalone mosquitto MQTT broker using docker-compose for further details on how to setup a mosquitto MQTT broker using this config.

If mosquitto is still printing the local only message even though you have listener 1883 in your config file, check if mosquitto is using the correct config file. In my case, I mis-spelled the config file path (conf instead of config), hence mosquitto used the default config file, not my config file and therefore ignored all statements I put in my config file.

Posted by Uli Köhler in MQTT, Networking

How to setup standalone mosquitto MQTT broker using docker-compose

docker-compose.yml

version: "3"

services:
  mosquitto:
    image: eclipse-mosquitto
    network_mode: host
    volumes:
      - ./conf:/mosquitto/conf
      - ./data:/mosquitto/data
      - ./log:/mosquitto/log

Now create conf/mosquitto.conf

persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log

listener 1883
## Authentication ##
# allow_anonymous false
password_file /mosquitto/conf/mosquitto.conf

Now create the first user using

docker-compose exec mosquitto mosquitto_passwd -c /mosquitto/conf/mosquitto.passwd mosquitto

You can optionally create more users using the -b (batch) flag instead of -c , supplying the password on the command line:

docker-compose exec mosquitto mosquitto_passwd -b /mosquitto/conf/mosquitto.passwd seconduser shoaCh3ohnokeathal6eeH2marei2o

Now start mosquitto using

docker-compose up

or create a systemd service to autostart it.

We are running mosquitto using network_mode: host. Mosquitto will, by default, listen on port 1883 (MQTT). You can configure more services using conf/mosquitto.conf, see this StackOverflow post for more info.

Posted by Uli Köhler in MQTT

How to setup Home-Assistant MQTT with username & password

The following setting in configuration.yml connects to a local mosquitto MQTT broker using username and password:

mqtt:
  broker: "127.0.0.1"
  username: "homeassistant"
  password: "iraughij3Phoh7ne9Aoxingi2eimoo"

Complete configuration.yml:

default_config:

http:
  use_x_forwarded_for: true
  trusted_proxies:
  - 127.0.0.1
  ip_ban_enabled: true
  login_attempts_threshold: 5

mqtt:
  broker: "127.0.0.1"
  username: "homeassistant"
  password: "iraughij3Phoh7ne9Aoxingi2eimoo"

# Text to speech
tts:
  - platform: google_translate

group: !include groups.yaml
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml

 

Posted by Uli Köhler in Home-Assistant, MQTT

How to fix mosquitto_passwd overwriting all other users in the password file

Problem:

When running mosquitto_passwd like this:

docker-compose exec mosquitto mosquitto_passwd /mosquitto/conf/mosquitto.passwd myuser

or with the -c parameter:

docker-compose exec mosquitto mosquitto_passwd -c /mosquitto/conf/mosquitto.passwd myuser

the user is created but all other users who where previously listed in the file are deleted.

Solution:

Create the first user using the -c flag in order to create the file if it does not exist

docker-compose exec mosquitto mosquitto_passwd -c /mosquitto/conf/mosquitto.passwd firstuser

Then create additional users using -b (batch mode),which allows you to specify the password on the command line:

docker-compose exec mosquitto mosquitto_passwd -b /mosquitto/conf/mosquitto.passwd seconduser shoaCh3ohnokeathal6eeH2marei2o

When using -b, old users will not be deleted.

Posted by Uli Köhler in MQTT

How to setup pfSense haproxy http-to-https redirect

Setting up HAProxy HTTP-to-HTTPS redirect is pretty simple:

  • Setup a new primary frontend. I typically name it HTTP-to-HTTPS but you can name it whatever you want
  • Configure the External address section to listen on port 80 on all interfaces you want to redirect. Note that you need to remove all port 80 listen addresses from all other primary frontends or else you won’t be able to reload haproxy

  • Now create an ACL http-request redirect rule with scheme https as the rule:

This will redirect any incoming request on this frontend to HTTPS. Since this frontend is only listening to HTTP, it will redirect all HTTP requests to HTTPS.

Posted by Uli Köhler in Networking

How to run System() in dialplan in the background on Asterisk/FreePBX

In Asterisk, a System() command like

exten => 123456,n,System(mycommand)

will generally run in the foreground, i.e. Asterisk will wait until the command is finished before continuing with the Dialplan.

However, just like in any normal Linux shell, you can add a & to the end to run the command in the background:

exten => 123456,n,System(mycommand &)

 

Posted by Uli Köhler in FreePBX

How to restart Asterisk from Dialplan in FreePBX

In our previous post How to add action on call to custom extension in FreePBX we showed how to get. Moreover, in our post on How to restart Asterisk in FreePBX we explored the three different options of restarting Asterisk, e.g. core restart gracefully.

In this post, we’ll add a dialplan extension (i.e. a number that you can dial) that restarts Asterisk. This will not restart FreePBX, but completely restart (and not only reload) Asterisk itself, loading any new configuration options and re-intializing all SIP trunks, for example. In this case we’re using core restart gracefully, i.e. we’ll be waiting for any currently active calls to finish, but not admit new calls.

Add this block to /etc/asterisk/extensions_custom.conf (see How to add action on call to custom extension in FreePBX):

[from-internal-additional-custom]
exten => 999999,1,Answer()
exten => 999999,n,Wait(0.5)
exten => 999999,n,SayAlpha(OK)
exten => 999999,n,System(bash -c "sleep 1 && asterisk -rx 'core restart gracefully'" &)
exten => 999999,n,Hangup()

Reload Asterisk and now you can call 999999 to restart Asterisk

How it works

Basically, we’re reading OK to the user and then running

System(bash -c "sleep 1 && asterisk -rx 'core restart gracefully'" &)

Basically, you could thing we’ll get away with just running

System(asterisk -rx 'core restart gracefully')

However, this will fail (or at least delay the restart for approximately one minute) because the call initiating the restart is still ongoing. Also, we can’t run System() after Hangup() because Hangup() will terminate the dialplan, hence System() will never run. Hence, we run it with a delay ( sleep 1) in the background (& at the end of the command), causing Asterisk to first cleanly hang up on the call and then the command will be run.

Note that the background & at the end is absolutely essential, omitting it will cause Asterisk to just wait for the sleep 1 && asterisk ... command to finish, not Hangup() in between.

Using bash -c to run the command is just a workaround so we can stuff the multiple chained commands of sleep 1 && asterisk ... into a single command which we can run in the background using the trailing &.

Posted by Uli Köhler in FreePBX

Is your PD device 802.3bt PoE++ Type-3 or Type-4?

Note: Do NOT confuse Type-3 or Type-4 with PoE class 3 and class 4!

If your PD uses a maximum of 51W (class 1-6 – up to 60W at the PSE), your device is Type 3. If your device uses 62W or 71.3W (class 7 or 8 – 75W or 90W at the PSE), your device is Type 4.

In case your device uses more than 71.3W (or 90W at the PSE), your use-case is not covered by IEEE 802.3bt aka PoE++.

Note that “uses” does not refer to the actual power draw of your device but whatever class you present during the PoE classification process. Typically, this is configured using a resistor, depending on your PoE PD controller.

For reference, see this Ethernet Alliance Whitepaper

Posted by Uli Köhler in Electronics, Networking, PoE

Does 802.3bt PoE++ work with 2-pair power or does it require 4-pair power?

802.3bt always requires four pairs for power >= 40W (class 5+) and does not support powering over two pairs. Powering 2-pair devices using 802.3bt will only when powering devices with up to 30W (i.e. in “802.3at backwards-compatible” mode).

For reference see this whitepaper by the Ethernet Alliance.

Posted by Uli Köhler in Electronics, Networking, PoE

What does “PSE” mean in PoE / Power over Ethernet context?

PD means Power sourcing equipment. This refers to a device which injects power in a PoE port – in other words, it provides Power over Ethernet as opposed to a PD (powered device) that consumes power injected by a Power sourcing equipment device.

Also see: What does “PD” mean in PoE / Power over Ethernet context?

Posted by Uli Köhler in Electronics, Networking, PoE

What does “PD” mean in PoE / Power over Ethernet context?

PD means Powered device. This refers to a device which is powered by a PoE port – in other words, it consumes Power over Ethernet as opposed to a PSE (power sourcing equipment) that injects power that can be consumed by powered devices.

Also see: What does “PSE” mean in PoE / Power over Ethernet context?

Posted by Uli Köhler in Electronics, Networking, PoE

How to run HTTP request on call to extension using FreePBX

This post is based on our previous post How to add custom action on call to custom extension in FreePBX

In FreePBX, add the following config to /etc/asterisk/extensions_custom.conf in order to add a custom extension 999 that uses System() to run curlon a fixed URL.

[from-internal-additional-custom]
exten => 999,1,Answer()
exten => 999,n,Wait(1)
exten => 999,n,System(curl http://mydomain.com/myurl)
exten => 999,n,Wait(1)
exten => 999,n,Hangup()

Don’t forget to reload Asterisk e.g. using the core reload command from the Administrator menu => Asterisk CLI in order for the changes to take effect.

For details on how [from-internal-additional-custom] works, see our previous post How to add action on call to custom extension in FreePBX

Posted by Uli Köhler in FreePBX

How to add action on call to custom extension in FreePBX

In FreePBX, add the following config to /etc/asterisk/extensions_custom.conf in order to add a custom extension 999 that just plays Hello world when called:

[from-internal-additional-custom]
exten => 999,1,Answer()
exten => 999,n,Wait(1)
exten => 999,n,Playback(hello-world)
exten => 999,n,Wait(1)
exten => 999,n,Hangup()

Don’t forget to reload Asterisk e.g. using the core reload command from the Administrator menu => Asterisk CLI in order for the changes to take effect.

How the dialplan routing works

Note that we have added this in the [from-internal-additional-custom] context, which is included at the beginning of the [from-internal-additional] context by FreePBX in extensions_additional.conf:

[from-internal-additional]
include => from-internal-additional-custom

The [from-internal-additional] context in turn is included in [from-internal]In other words, our custom extension 999 will be active for all phones calling from the [from-internal] context – which is, by default, all internal extensions.

Posted by Uli Köhler in FreePBX

How to fix python ipaddress.IPv6Address AddressValueError: Unexpected ‘/’ in ‘…/64’

Problem:

When trying to parse an IPv6 network address in Python using code like

import ipaddress
ipaddress.IPv6Network("2a01:c23:c0bb:d00:8ce6:2eff:fe60:cc69/64")

you see an error message like

---------------------------------------------------------------------------
AddressValueError                         Traceback (most recent call last)
/tmp/ipykernel_154945/2602627019.py in <module>
----> 1 ipaddress.IPv6Address("2a01:c23:c0bb:d00:8ce6:2eff:fe60:cc69/64")

/usr/lib/python3.8/ipaddress.py in __init__(self, address)
   1836         addr_str = str(address)
   1837         if '/' in addr_str:
-> 1838             raise AddressValueError("Unexpected '/' in %r" % address)
   1839         self._ip = self._ip_int_from_string(addr_str)
   1840 

AddressValueError: Unexpected '/' in '2a01:c23:c0bb:d00:8ce6:2eff:fe60:cc69/64'

Solution 1: Remove the CIDR netmask (/64)

By just removing the slash and the part after it (the CIDR netmask).

Solution 1: Maybe you should use IPv6Network instead of IPv6Address

If you intend to parse the network, use ipaddress.IPv6Network but remember that this will discard all host bits. If you want to use IPv6Address or IPv6Network really depends on what you want to do with it – if you want to refer to a specific host, you almost always want to use IPv6Address.

import ipaddress
ipaddress.IPv6Network("2a01:c23:c0bb:d00:8ce6:2eff:fe60:cc69/64", strict=False)

Note that strict=False is added in order to prevent an exception due to host bits being set – see How to fix Python ipaddress.IPv6Network ValueError: … has host bits set

Posted by Uli Köhler in Networking, Python

How to fix Python ipaddress.IPv6Network ValueError: … has host bits set

Problem:

When trying to parse an IPv6 network address in Python using code like

import ipaddress
ipaddress.IPv6Network("2a01:c23:c0bb:d00:8ce6:2eff:fe60:cc69/64")

you see an error message like

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/tmp/ipykernel_154945/1312927855.py in <module>
      1 import ipaddress
----> 2 ipaddress.IPv6Network("2a01:c23:c0bb:d00:8ce6:2eff:fe60:cc69/64")

/usr/lib/python3.8/ipaddress.py in __init__(self, address, strict)
   2106         if packed & int(self.netmask) != packed:
   2107             if strict:
-> 2108                 raise ValueError('%s has host bits set' % self)
   2109             else:
   2110                 self.network_address = IPv6Address(packed &

ValueError: 2a01:c23:c0bb:d00:8ce6:2eff:fe60:cc69/64 has host bits set

Solution 1: Maybe you should use IPv6Address instead of IPv6Network

If you intend to parse the address and don’t care about the network, use ipaddress.IPv6Address but remember that the CIDR mask (e.g. /64)  needs to be removed. If you want to use IPv6Address or IPv6Network really depends on what you want to do with it – if you want to refer to a specific host, you almost always want to use IPv6Address.

import ipaddress
ipaddress.IPv6Address("2a01:c23:c0bb:d00:8ce6:2eff:fe60:cc69")

Solution 2: Use strict=False to let IPv6Network discard the host bits:

strict=False basically ignores this error

import ipaddress
ipaddress.IPv6Network("2a01:c23:c0bb:d00:8ce6:2eff:fe60:cc69/64", strict=False)

Note that the result will be

IPv6Network('2a01:c23:c0bb:d00::/64')

so the information in the host bits will be lost!

Solution 2: Remove the host bits manually

Since the host bits will be discarded anyway, you can just specify the IPv6 network with the correct netmask:

import ipaddress
ipaddress.IPv6Network("2a01:c23:c0bb:d00::/64")

 

Posted by Uli Köhler in Networking, Python