Code Monkey home page Code Monkey logo

ir2mqtt's Introduction

ir2mqtt

This application can publish iRacing session an telemetry data on mqtt topics. So yor're able to integrate iRacing into many smart home solutions. In addition I implemented a small serial protocol which enables you to integrate with an Arduino-/Teensy-based buttonbox to reflect pit flags (tyre change, re-fuel etc.) and refueling amount.

In addition the astronomical twilight times and sun elevation angles are calculated by using the geographical track locations from the irsdk information.

My first appliance is to control room lighting to correspond to the simulations TimeOfDay so it switches off during the night and is dimmed during sunrise/sunset.

Prerequisites

You need to install

Optionally if you wand to create a self contained executable you may install PyInstaller. Note that due to a bug you will need to install the develpment version:

pip install https://github.com/pyinstaller/pyinstaller/tarball/develop

Building

No build steps are necessary to execute the python script:

python ir2mqtt.py

To create a self-contained executable:

pyinstaller --clean -F --add-binary "<your python path>\Lib\site-packages\timezonefinder;timezonefinder" ir2mqtt.py

Configuration

Configuration is done by a configuration file ir2mqtt.ini which is expected in the same directory as the script/application is executed.

[DEFAULT]

[global]
# Generates additional debug output. All MQTT and serial communication will be 
# logged. Comment out or set to yes/True to enable
debug = no

# Serial communication port to send data to. Comment out to disable serial
# communication
;serial = COM3

# Uncomment to start the application using a data dump file from irsdk for 
# testing/development purposes. The dump file can be created by issuing the 
# command 'irsdk --dump data.dmp'
;simulate = data/limerock-q.dmp

[mqtt]
# Hostname of a MQTT broker to connect to. Comment out to disable MQTT 
# communication.
host = localhost

# TCP port on which a MQTT broker is listening. Default is 1883.
port = 1883

# MQTT topic prefix to use when publishing values - see section [iracing]
baseTopic = /sensors/iRacing

# Timezone used to publish the simulations TimeOfDay
timezone=CET

[iracing]
# Mapping of irsdk values to MQTT topics.
# Format: mqttTopic = irsdkField
# The configuration key (mqtt topic is prepended by the baseTopic configuration 
# value). So a configuration line
#
# eventType = WeekendInfo/EventType
#
# will post the EventType value from the iRacing WeekendInfo data structure on
# the MQTT topic '/sensors/iRacing/eventType'
#
# For accessing list structures (e.g. Sessions in SessionInfo) you have to use 
# array notation:
#
# sessionType = SessionInfo/Sessions[0]/SessionType
#
# You can use 'last' as a special index value to retrieve the last element from
# the list. 
#
# Furthermore, you may use an other telemetry value as list index. Simply 
# prefix that value with a hast mark '#':
#
# sessionType = SessionInfo/Sessions[#SessionNum]/SessionType

eventType = WeekendInfo/EventType
practiceTrackState = SessionInfo/Sessions[0]/SessionTrackRubberState
currentSessionType = SessionInfo/Sessions[#SessionNum]/SessionType

State and TimeOfDay publishing

Indepentently from the configuration to the application the IRSDK connection state is published on the topic

<baseTopic>/state 

as numerical value:

  • 0 means irsdk is disconnected from the simulation
  • 1 means irsdk is connected to the simulation

The date and time of day information is translated from the original track timezone to the timezone specified in the configuration and published on the topic

<baseTopic>/ToD 

in ISO format:

%Y-%m-%dT%H:%M:%S<+/-ffff>

For example Noon at 2nd of March 2019 in the CEST timezone is published as

2019-03-02T12:00:00+0200

Light information publishing

Independently from the configuration a lightinfo information is calculated which can be one of the following values: dusk, day, dawn, night. This value is published on the MQTT topic:

<baseTopic>/lightinfo

The current elevation angle of the sun for the simulation time of day at the track location is published as a float value on the topic:

<baseTopic>/solarElevation

Negative value means the sun is below the horizon, positive value means it's above.

Serial telegram protocol

The protocol is string-based to enable best integration an interoperability possibilities.

The basic form is

*KEY=VALUE#

There is no length assumption on the KEY and VALUE part but I decided to use short 3-letter keys because of the limited memory capacities for a microcontroller.

Currently three telegrams are supported:

*PFL=<int>#   (outbound)
*PFU=<int>#   (inbound/outbound)
*PCM=<int>#   (inbound)

Outbound means communication from application to microcontroller, inbound means microcontroller to application.

PFL telegram

This telegram submits the PitSvFlags telemetry value. The Number submitted can be evaluated by bit comparison defined in irsdk.py:

lf_tire_change     = 0x01
rf_tire_change     = 0x02
lr_tire_change     = 0x04
rr_tire_change     = 0x08
fuel_fill          = 0x10
windshield_tearoff = 0x20
fast_repair        = 0x40

PFU telegram

This telegram submits the PitSvFuel telemetry value. The number submitted is the current amount of fuel to be added during next pit stop. The unit of the value depends on the simulation configuration and is not taken into account in any way.

On the receiving side this telegram triggers the

irsdk.pit_command(PitCommandMode.fuel, <telegramValue>)

function, submitting the telegram's VALUE as fuel amount to add. As implemented in IRSDK this amount is always taken in liters as unit.

PCM telegram

This telegram receives a pit command and triggers the

irsdk.pit_command(<telegramValue>)

function. The usable parameters are:

clear       =  0 # Clear all pit checkboxes
ws          =  1 # Clean the winshield, using one tear off
fuel        =  2 # Add fuel, optionally specify the amount to add in liters or pass '0' to use existing amount
lf          =  3 # Change the left front tire, optionally specifying the pressure in KPa or pass '0' to use existing pressure
rf          =  4 # right front
lr          =  5 # left rear
rr          =  6 # right rear
clear_tires =  7 # Clear tire pit checkboxes
fr          =  8 # Request a fast repair
clear_ws    =  9 # Uncheck Clean the winshield checkbox
clear_fr    = 10 # Uncheck request a fast repair
clear_fuel  = 11 # Uncheck add fuel

Please note that a second parameter is not supported in the current implementation, so there is currently to way to change tyre pressures. For changing the refuel amount see PFU telegram.

Reading/writing telegrams on Arduino/Teensy

I provide an Arduino library IRserial which should help to implement a Sketch communicating with the iRacing simulation. Als alternative - here a function you can use in a sketch to read the telegrams from the serial port on an Arduino/Teensy microcontroller:

String readTelegramFromSerial() {
  char buff[10];
  int dataCount = 0;
  boolean startData = false;
  while(Serial.available()) {
    char c = Serial.read();
    if( c == '#' ) {
      startData = true;
    } else if( startData && dataCount < 10) {
      if( c != '*') {
        buff[dataCount++] = c;
      } else {  
        break;
      }
    } else if(dataCount >= 10) {
      return String();
    }
  }
  if( startData || dataCount > 0 ) {
    return String(buff);
  }
  return String();
}

The function returns the telegram content excluding the start and end marker characters '#' and ''. For example the serial telegram '#PFU=10' is returned as PFU=10. You can separate the key and value with simple string operations:

void processTelegram(String* telegram) {
  int idx = telegram->indexOf('=');
  if( idx > 0 ) {
    String key = telegram->substring(0, idx);
    String val = telegram->substring(idx+1);
    if( key.equals("PFL") ){
      processFlags(val.toInt());
    } else if( key.equals("PFU") ) {
      processFuel(val.toFloat());
    }
  }
}

You have to provide the processFlags and processFuel functions to handle your controller specific actions.

To evaluate the pit service flags, this structure may be helpful:

struct PitSvFlags {
    byte lf_tire_change;
    byte rf_tire_change;
    byte lr_tire_change;
    byte rr_tire_change;
    byte fuel_fill;
    byte windshield_tearoff;
    byte fast_repair;
};
    
PitSvFlags pitFlags = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40};

On the sending site the pit command numbers may be helpful:

struct PitCommand {
    clear;       // Clear all pit checkboxes
    ws;          // Clean the winshield, using one tear off
    fuel;        // Add fuel, optionally specify the amount to add in liters or pass '0' to use existing amount
    lf;          // Change the left front tire, optionally specifying the pressure in KPa or pass '0' to use existing pressure
    rf;          // right front
    lr;          // left rear
    rr;          // right rear
    clear_tires; // Clear tire pit checkboxes
    fr;          // Request a fast repair
    clear_ws;    // Uncheck Clean the winshield checkbox
    clear_fr;    // Uncheck request a fast repair
    clear_fuel;  // Uncheck add fuel
}

PitCommand pitCmd = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};

A sending function can be implemented as follows:

void sendPitCmd(int cmd) {
  Serial.print("#PCM=" + String(cmd) + "*\n");
}

void sendPitFuelCmd(int amount) {
  Serial.print("#PFU=" + String(amount) + "*\n");
}

Please not the trailing newline character. It is required as ir2mqtt uses the serial readline() function to receive telegrams.

ir2mqtt's People

Contributors

robbyb67 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

ir2mqtt's Issues

Timezone error running the Limerock test dump

Howdy, I'm trying to get this up and running and having an issue with the running the test file. Its blowing up at what looks to be session start, trying to handle the time zone from the track. Here's the console from the whole session.

I'm running the default base config, I've only changed the MQTT broker and added a few basic telemetry parameters.

Note, there was an error earlier in the session, but the time zone one is what kills the process.

=============================
|         IR2MQTT           |
|           1.7             |
=============================
MQTT host: oasserver
MQTT port: 1883
MQTT base: /sensors/iRacing
Debug output enabled
starting up using dump file: data/limerock-q.dmp
irsdk connected
Location:  Observer(latitude=41.928641, longitude=-73.38105, elevation=164.01)
LocationInfo:  LocationInfo(name='Lakeville', region='USA', timezone='America/New_York', latitude=41.928641, longitude=-73.38105)
sunrise start  2019-04-01 06:04:35.349539-04:00
sunrise end    2019-04-01 06:34:53.895901-04:00
sunset start 2019-04-01 19:20:37.716530-04:00
sunset end   2019-04-01 19:50:31.822780-04:00
Location:  Observer(latitude=41.928641, longitude=-73.38105, elevation=164.01)
LocationInfo:  LocationInfo(name='Lakeville', region='USA', timezone='America/New_York', latitude=41.928641, longitude=-73.38105)
MQTT: Connection successful
DEBUG mqtt_publish(/sensors/iRacing/state, 1)
Exception in thread Thread-1 (_thread_main):
Traceback (most recent call last):
  File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.10/threading.py", line 953, in run
    self._target(*self._args, **self._kwargs)
  File "/home/dave/dev/ir2mqtt/venv/lib/python3.10/site-packages/paho/mqtt/client.py", line 3591, in _thread_main
    self.loop_forever(retry_first_connection=True)
  File "/home/dave/dev/ir2mqtt/venv/lib/python3.10/site-packages/paho/mqtt/client.py", line 1756, in loop_forever
    rc = self._loop(timeout)
  File "/home/dave/dev/ir2mqtt/venv/lib/python3.10/site-packages/paho/mqtt/client.py", line 1164, in _loop
    rc = self.loop_read()
  File "/home/dave/dev/ir2mqtt/venv/lib/python3.10/site-packages/paho/mqtt/client.py", line 1556, in loop_read
    rc = self._packet_read()
  File "/home/dave/dev/ir2mqtt/venv/lib/python3.10/site-packages/paho/mqtt/client.py", line 2439, in _packet_read
    rc = self._packet_handle()
  File "/home/dave/dev/ir2mqtt/venv/lib/python3.10/site-packages/paho/mqtt/client.py", line 3039, in _packet_handle
    return self._handle_connack()
  File "/home/dave/dev/ir2mqtt/venv/lib/python3.10/site-packages/paho/mqtt/client.py", line 3138, in _handle_connack
    on_connect(
  File "/home/dave/dev/ir2mqtt/ir2mqtt.py", line 330, in on_connect
    if state.local_date_time == -1:
AttributeError: 'State' object has no attribute 'local_date_time'
DEBUG mqtt_publish(/sensors/iRacing/eventtype, Race)
DEBUG mqtt_publish(/sensors/iRacing/practicetrackstate, carry over)
DEBUG mqtt_publish(/sensors/iRacing/currentsessiontype, Lone Qualify)
DEBUG mqtt_publish(/sensors/iRacing/ingarage, False)
DEBUG mqtt_publish(/sensors/iRacing/onpitroad, False)
DEBUG mqtt_publish(/sensors/iRacing/brake, 1.0)
DEBUG mqtt_publish(/sensors/iRacing/gear, 0)
DEBUG mqtt_publish(/sensors/iRacing/steeringwheelangle, 0.0)
session ToD: 2019-04-01T07:30:03  TZ: America/New_York
DEBUG mqtt_publish(/sensors/iRacing/ToD, 2019-04-01T11:30:03+0000)
local time:  2019-04-01 07:30:03-04:00
solar elevation: 9.179465919503954
DEBUG mqtt_publish(/sensors/iRacing/solarElevation, 9.179465919503954)
Traceback (most recent call last):
  File "/home/dave/dev/ir2mqtt/ir2mqtt.py", line 411, in <module>
    loop()
  File "/home/dave/dev/ir2mqtt/ir2mqtt.py", line 248, in loop
    publish_session_time()
  File "/home/dave/dev/ir2mqtt/ir2mqtt.py", line 141, in publish_session_time
    publish_light_info()
  File "/home/dave/dev/ir2mqtt/ir2mqtt.py", line 153, in publish_light_info
    date_and_time = datetime.astimezone(state.ir_location.timezone)
TypeError: descriptor 'astimezone' for 'datetime.datetime' objects doesn't apply to a 'America/New_York' object

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.