Skip to main content

A library for managing and remotely controlling devices connected to one or several bridges, which in turn are communicated with via a specialized I2C protocol. Intended for a Smart Home FPGA solution that was created as part of a Bachelor's thesis.

Project description

FPGA I2C Bridge API

A library for managing and remotely controlling devices connected to one or several bridges, which in turn are communicated with via a specialized I²C protocol. Intended for a Smart Home FPGA solution that was created as part of a Bachelor's thesis.

Includes a high-level API as well as a low-level debugging shell.

Installation

pip install fpga-i2c-bridge

Usage

Interactive shell

On a device with I2C support, run:

python -m fpga_i2c_bridge

Use the --addr and --bus switches to adjust I2C bus and address values, respectively.

To run with simulated dummy devices only, run instead:

python -m fpga_i2c_bridge --dummy

Use --dummy-appliances and --dummy-sensors to supply a comma-seperated list of device types to simulate. For example:

python -m fpga_i2c_bridge --dummy --dummy-appliances 1,2,3,4 --dummy-sensors 1,3,5

API Examples

Reading and manipulating device states

from fpga_i2c_bridge import I2CBridge, I2CGenericBinary, I2CDimmer, I2CRGBDimmer, I2CShutter

# Appliance IDs
LAMP_ID = 1
DIMMER_ID = 2
RGB_ID = 3
SHUTTER_ID = 4

if __name__ == '__main__':
    # Create and initialize I2C Bridge
    i2c = I2CBridge(i2c_bus=1, i2c_addr=0x3E)
    
    # Store device objects
    lamp: I2CGenericBinary = i2c.appliances[LAMP_ID]
    dimmer: I2CDimmer = i2c.appliances[DIMMER_ID]
    rgb: I2CRGBDimmer = i2c.appliances[RGB_ID]
    shutter: I2CShutter = i2c.appliances[SHUTTER_ID]
    
    # -- Read device states --
    print(f"Lamp is currently {'on' if lamp.state is True else 'off'}")
    print(f"Dimmer brightness: {dimmer.state}")
    print(f"RGB lamp color: {rgb.state}")
    print(f"Shutter state: {shutter.state}")
    
    # -- Manipulate devices --
    # Turn on lamp
    lamp.turn_on()
    
    # Set dimmer to 50% brightness
    dimmer.set_brightness(0.5)
    
    # Set RGB lamp to orange-ish
    rgb.set_color((1.0, 0.7, 0.0))
    
    # Move shutter up fully
    shutter.move_up_full()

Using callback handlers

from fpga_i2c_bridge import I2CBridge, I2CShutter, I2CShutterControlSensor

# Assuming a shutter is found under appliance ID 4
SHUTTER_ID = 4

# Assuming a shutter is found under sensor ID 0
SHUTTER_SENSOR_ID = 0

if __name__ == '__main__':
    # Create and initialize I2C Bridge
    i2c = I2CBridge(i2c_bus=1, i2c_addr=0x3E)

    # Store device objects
    shutter_appliance: I2CShutter = i2c.appliances[SHUTTER_ID]
    shutter_sensor: I2CShutterControlSensor = i2c.sensors[SHUTTER_SENSOR_ID]

    # Register handlers for the shutter sensor
    @shutter_sensor.register_full_down_handler()
    def on_full_down():
        print("Request to move down")
        shutter_appliance.move_down_full()

    @shutter_sensor.register_full_up_handler()
    def on_full_up():
        print("Request to move up")
        shutter_appliance.move_up()

    # Register handler for the shutter appliance
    @shutter_appliance.register_update_handler()
    def on_appliance_update():
        print(f"The shutter {shutter_appliance} updated its state to {shutter_appliance.state}")

    # Enable polling
    i2c.start_polling()

    # Loop and wait for input
    print("Press CTRL-C to quit.")
    try:
        while True:
            pass
    except KeyboardInterrupt:
        pass

Protocol specification

This section describes the protocol used for communications between the device using this library (hereafter referred to as the "gateway") and a connected I2C device (the "bridge").

The bridge operates as an I2C slave on any address (by default, the library assumes this address to be 0x3E, but this can be changed via the --addr command line switch, as outlined above.)

Commands

Command messages sent to the I2C bridge consist of a command-dependent opcode byte as well as several parameter bytes. The amount of parameter data depends on the command used, and can range between 0 and 4 bytes in length.

Finally, 2 bytes of CRC data are appended at the end of the message, calculated from the opcode and parameter bytes using the polynomial x^16 + x^13 + x^11 + x^10 + x^9 + x^8 + x^4 + x^2 + 1 (encoded as 0x2F15 when omitting the MSB).

The following table gives a brief overview over the different commands, their opcodes and parameters:

Command Opcode Parameters Description
Get appliance state 0x00 Appliance ID Query an appliance's current state
Get appliance type 0x01 Appliance ID Query an appliances' type
Get sensor type 0x02 Sensor ID Query a sensor's type
Set appliance state 0x10 Appliance ID, new state Set state of an appliance
Get bridge status 0x20 - Queries bridge version and device count
Reset bridge 0x2F - Reset bridge hardware
Poll events 0x30 - Poll for new input and update events
Repeat last message 0x40 - Request last message to be sent again

The bridge prepares responses to each command, which do not necessarily have to be read by the gateway. Responses are always 8 bytes long and contain the following:

Byte Meaning
1 Status code: OK (0xF0), Error (0xF1), No data (0xF2)
2..6 Response data (padded with 0x00 to 5 bytes)
7..8 CRC data

Should a command result in an error, the response data includes the error codes and some relevant parameters. (See appendix.)

Get appliance state (0x00)

Requests the current state of an appliance.

  • Parameters: Appliance ID (1 byte)
  • Expected response: Appliance ID repeated (1 byte), Appliance state (3 bytes)
  • Error cases:
    • If an unknown appliance ID is supplied, responds with error code 0x20 (Unknown device) followed by the erroneous appliance ID
Example
Command Response
00 01 2F 15 F0 01 00 00 01 00 4F 38
Get state of appliance 0x01 OK, Appliance 0x01 state is 0x000001
Example for error on unknown appliance
Command Response
00 04 BC 54 F1 20 04 00 00 00 2C 57
Get state of appliance 0x04 Error: Unknown appliance 0x04

Get appliance type (0x01)

Requests the type of an appliance.

  • Parameters: Appliance ID (1 byte)
  • Expected response: Appliance ID repeated (1 byte), Appliance type identifier (1 bytes, see appendix) or 0x00 if the given appliance ID is not present
  • Error cases:
    • If an unknown appliance ID is supplied, responds with error code 0x20 (Unknown device) followed by the erroneous appliance ID
Example
Command Response
01 01 D1 22 F0 01 02 00 00 00 75 8B
Get type of appliance 0x01 OK, Appliance 0x01 is a Dimmer (0x02)
Example for error on unknown appliance
Command Response
01 FF B1 29 F1 20 FF 00 00 00 D4 71
Get type of appliance 0xFF Error: Unknown appliance 0xFF

Get sensor type (0x02)

Requests the type of a sensor.

  • Parameters: Sensor ID (1 byte)
  • Expected response: Sensor ID repeated (1 byte), Sensor type identifier (1 bytes, see appendix)
  • Error cases:
    • If an unknown sensor ID is supplied, responds with error code 0x20 (Unknown device) followed by the erroneous sensor ID
Example
Command Response
02 00 D3 7B F0 00 01 00 00 00 F7 ED
Get type of sensor 0x00 OK, Sensor 0x00 is a Button (0x01)
Example for error on unknown sensor
Command Response
02 FF 9C 65 F1 20 FF 00 00 00 D4 71
Get type of sensor 0xFF Error: Unknown sensor 0xFF

Set appliance state (0x10)

Requests to set the state of an appliance.

  • Parameters: Appliance ID (1 byte), new state (3 bytes)
  • Expected response: Empty response
  • Error cases:
    • If an unknown appliance ID is supplied, responds with error code 0x20 (Unknown device) followed by the erroneous appliance ID
Example
Command Response
10 02 FF 77 00 C7 6C F0 00 00 00 00 00 7D 3E
Set appliance 0x02 to state 0xFF7700 OK
Example for error on unknown appliance
Command Response
10 49 12 34 56 4A 63 F1 20 49 00 00 00 A2 25
Set appliance 0x49 to state 0x123456 Error: Unknown appliance 0x49

Get bridge status (0x20)

Retrieves the bridge version as well as the highest valid appliance and sensor IDs each. Depending on the configuration, not all device IDs are necessarily assigned.

  • Parameters: None
  • Expected response: Bridge version (2 bytes), highest appliance ID (1 byte), highest sensor ID (1 byte)
  • Error cases: None
Example
Command Response
20 71 E1 F0 DE AD 04 05 00 53 73
Get status OK, version is 0xDEAD, highest appliance ID is 0x04, highest sensor ID is 0x05

Reset bridge (0x2F)

Requests the bridge hardware to reinitialize. What this means is up to the bridge implementation, but usually should put the hardware into a sane state. Reinitialization should also not take more than a couple of cycles.

  • Parameters: None
  • Expected response: Empty
  • Error cases: None
Example
Command Response
2F EB 37 F0 00 00 00 00 00 7D 3E
Reset bridge OK

Poll events (0x30)

Receives a single input or update event from the bridge, if there are any. An update event describes an intrinsic, spontaneous change of an appliance's state without user input, while input events are fired by a sensor that has been triggered.

If there are no pending events, the bridge is expected to send a "No data" (0xF2) status response. The API keeps repeatedly sending this command until it receives such a response, to clear any event buffers the bridge might have.

  • Parameters: None
  • Expected response: Event data (5 bytes, see appendix) or "No data"
  • Error cases: None
Example: Receiving an input event
Command Response
30 DE 9B F0 00 01 00 00 01 D8 F8
Poll for event OK, Input event (0x00) for sensor ID 0x01, data: 0x000001
Example: Receiving an update event
Command Response
30 DE 9B F0 01 03 00 00 00 FF 58
Poll for event OK, Update event (0x01) for appliance ID 0x03, new state: 0x000000
Example: No new events
Command Response
30 DE 9B F2 00 00 00 00 00 5F 49
Poll for event No data

Repeat last message (0x40)

Requests the last response to be sent again. This is used by the API in case it encounters a CRC checksum mismatch in any response. The bridge should always keep track of the last response it sent (that was not a CRC error response) and repeat it.

Sending this command intentionally before any other command results in undefined behavior.

  • Parameters: none
  • Expected response: Depends on the previous command
  • Error cases: Depends on the previous command
Example: Repeating a previous response in case of a CRC error
Command Response
10 00 00 00 01 7E 4A F0 00 00 00 00 10 7D 3E
Set appliance 0x00 to state 0x000001 OK (corrupted)
40 E3 C2 F0 00 00 00 00 00 7D 3E
Repeat last message OK (valid)

Appendix

Error response codes

Code Error Parameters
0x10 Unknown command opcode Unrecognized opcode, repeated
0x20 No such device Unrecognized appliance or sensor ID, repeated
0x30 CRC failure, please repeat Calculated (nonzero) checksum of received message
0xFF Unknown failure -

Event data

Input events

An input event gets fired by a sensor that has been triggered, e.g. when a button has been pressed, or similar. The event contains the ID of the sensor as well as the data it is reporting.

Byte Content
1 0x00
2 ID of sensor that fired event
3..5 Sensor payload
Update events

An update event gets fired when an appliance has changed its state by itself, thus notifying the API about its new state. The event contains the ID of the appliance as well as its new state.

Byte Content
1 0x01
2 ID of appliance that changed state
3..5 New state of appliance

Appliance type identifiers

Code Appliance type
0x01 Switch
0x02 Dimmer
0x03 RGB Dimmer
0x04 Shutter

Sensor type identifiers

Code Sensor type
0x01 Button
0x02 Toggle
0x03 Dimmer cycle
0x04 RGB cycle
0x05 Shutter control

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fpga-i2c-bridge-0.2.5.tar.gz (25.5 kB view hashes)

Uploaded Source

Built Distribution

fpga_i2c_bridge-0.2.5-py3.8.egg (58.4 kB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page