├── README.md ├── Schematic.jpg ├── enclosure.JPG ├── main.cpp └── platformio.ini /README.md: -------------------------------------------------------------------------------- 1 | # SensESP Engine Monitor 2 | 3 | *** A version of this updated for SesnseESP Version 3 can be found [here](https://github.com/Techstyleuk/SensESP_V3-Engine_Monitor) *** 4 | 5 | This repository provides a copy of 'main.cpp' and 'platformio.ini' to be used in conjuction with the template for [SensESP](https://github.com/SignalK/SensESP/) projects. 6 | download the files and try building and uploading the project to an ESP32 device. 7 | You should immediately see output on the serial monitor! 8 | 9 | The files should replace the files of the same name in the template. locations are as follows: 10 | 11 | src/main.cpp 12 | 13 | platformio.ini 14 | 15 | Comprehensive documentation for SensESP, including how to get started with your own project, is available at the [SensESP documentation site](https://signalk.org/SensESP/). 16 | 17 | See [Schematic.jpg](https://github.com/Techstyleuk/sensesp-engine_monitor/blob/main/Schematic.jpg) for the schematic 18 | 19 | A Youtube video to accompany this project will be available on the Apres channel at https://www.youtube.com/@ApresSail 20 | 21 | 22 | # Parts List 23 | - [Firebeetle ESP32-E](https://www.dfrobot.com/product-2231.html) 24 | - [Power Board](https://amzn.to/3rvaHif) 25 | - [1-Wire Sensors](https://amzn.to/3LnNl62) 26 | - [Waveshare BME280](https://amzn.to/3Gn2dzX) 27 | - [RPM Optocoupler](https://amzn.to/3GuPF7m) 28 | - Project Box 29 | -------------------------------------------------------------------------------- /Schematic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Techstyleuk/sensesp-engine_monitor/75c987d1c356a9673f40a866bc096d334f2b38d8/Schematic.jpg -------------------------------------------------------------------------------- /enclosure.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Techstyleuk/sensesp-engine_monitor/75c987d1c356a9673f40a866bc096d334f2b38d8/enclosure.JPG -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | // SenESP Engine Monitor 2 | // Production Code for Apres' Engine Monitor 3 | // Last modification was to add Bilge monitor code 4 | 5 | #include //JG Added 6 | #include 7 | #include "sensesp_onewire/onewire_temperature.h" 8 | #include 9 | #include "sensesp/sensors/analog_input.h" 10 | #include "sensesp/sensors/digital_input.h" 11 | #include "sensesp/sensors/sensor.h" 12 | #include "sensesp/signalk/signalk_output.h" 13 | #include "sensesp/system/lambda_consumer.h" 14 | #include "sensesp_app_builder.h" 15 | #include "sensesp/transforms/linear.h" 16 | #include "sensesp/transforms/analogvoltage.h" 17 | #include "sensesp/transforms/curveinterpolator.h" 18 | #include "sensesp/transforms/voltagedivider.h" 19 | #include "sensesp/transforms/frequency.h" 20 | #include "sensesp/transforms/lambda_transform.h" 21 | 22 | using namespace sensesp; 23 | 24 | class FuelInterpreter : public CurveInterpolator { 25 | public: 26 | FuelInterpreter(String config_path = "") 27 | : CurveInterpolator(NULL, config_path) { 28 | // Populate a lookup table to translate RPM to M^3/s 29 | clear_samples(); 30 | // addSample(CurveInterpolator::Sample(RPM, M^3/s)); 31 | add_sample(CurveInterpolator::Sample(500, 0.00000473125946250)); 32 | add_sample(CurveInterpolator::Sample(1000, 0.00000630834595000)); 33 | add_sample(CurveInterpolator::Sample(1500, 0.00001261669190000)); 34 | add_sample(CurveInterpolator::Sample(1800, 0.00001577086487500)); 35 | add_sample(CurveInterpolator::Sample(2000, 0.00001955587244500)); 36 | add_sample(CurveInterpolator::Sample(2100, 0.00002207921082500)); 37 | add_sample(CurveInterpolator::Sample(2500, 0.00004100424867500)); 38 | add_sample(CurveInterpolator::Sample(2600, 0.00004731259462500)); 39 | add_sample(CurveInterpolator::Sample(2800, 0.00005677511355000)); 40 | add_sample(CurveInterpolator::Sample(3000, 0.00006939180545000)); 41 | add_sample(CurveInterpolator::Sample(3200, 0.00008831684330000)); 42 | add_sample(CurveInterpolator::Sample(3400, 0.00010724188115000)); 43 | } 44 | }; 45 | 46 | // Oil Pressure lookup 47 | class PressureInterpreter : public CurveInterpolator { 48 | public: 49 | PressureInterpreter(String config_path = "") 50 | : CurveInterpolator(NULL, config_path) { 51 | // Populate a lookup table to translate the ohm values returned by 52 | // our Pressure sender to Pascal 53 | clear_samples(); 54 | // addSample(CurveInterpolator::Sample(knownOhmValue, knownPascal)); 55 | add_sample(CurveInterpolator::Sample(10, 0)); 56 | add_sample(CurveInterpolator::Sample(21, 50000)); 57 | add_sample(CurveInterpolator::Sample(31, 100000)); 58 | add_sample(CurveInterpolator::Sample(42, 150000)); 59 | add_sample(CurveInterpolator::Sample(52, 200000)); 60 | add_sample(CurveInterpolator::Sample(71, 300000)); 61 | add_sample(CurveInterpolator::Sample(90, 400000)); 62 | add_sample(CurveInterpolator::Sample(107, 500000)); 63 | add_sample(CurveInterpolator::Sample(124, 600000)); 64 | add_sample(CurveInterpolator::Sample(140, 700000)); 65 | add_sample(CurveInterpolator::Sample(156, 800000)); 66 | add_sample(CurveInterpolator::Sample(163, 850000)); 67 | add_sample(CurveInterpolator::Sample(170, 900000)); 68 | add_sample(CurveInterpolator::Sample(184, 1000000)); 69 | } 70 | }; 71 | 72 | reactesp::ReactESP app; 73 | 74 | 75 | // BME280 76 | 77 | Adafruit_BME280 bme280; 78 | 79 | float read_temp_callback() { return (bme280.readTemperature() + 273.15);} 80 | float read_pressure_callback() { return (bme280.readPressure());} 81 | float read_humidity_callback() { return (bme280.readHumidity());} 82 | 83 | 84 | // The setup function performs one-time application initialization. 85 | void setup() { 86 | #ifndef SERIAL_DEBUG_ENABLED 87 | SetupSerialDebug(115200); 88 | #endif 89 | 90 | Wire.begin(21,22); // join i2c bus (address optional for master) 91 | // 92 | //Serial.begin(9600); // start serial communication at 9600bps 93 | 94 | //Serial.println(F("BME280 Forced Mode Test.")); 95 | 96 | //if (!bmp.begin(BMP280_ADDRESS_ALT, BMP280_CHIPID)) { 97 | //if (!bme280.begin()) { 98 | //Serial.println(F("Could not find a valid BMP280 sensor, check wiring or " 99 | // "try a different address!")); 100 | //while (1) delay(10);// could need a delay here: 101 | //} 102 | // 103 | 104 | // Construct the global SensESPApp() object 105 | SensESPAppBuilder builder; 106 | sensesp_app = (&builder) 107 | // Set a custom hostname for the app. 108 | ->set_hostname("Apres-Eng-Mon") 109 | // Optionally, hard-code the WiFi and Signal K server 110 | // settings. This is normally not needed. 111 | //->set_wifi("My WiFi SSID", "my_wifi_password") 112 | //->set_sk_server("192.168.10.3", 80) 113 | ->enable_uptime_sensor() 114 | ->get_app(); 115 | 116 | /// 1-Wire Temp Sensors - Exhaust Temp Sensors /// 117 | 118 | DallasTemperatureSensors* dts = new DallasTemperatureSensors(25); //digital 2 119 | 120 | // Oil temp (fasten to oil P sensor) - /propulsion/engine/oilTemperature 121 | auto* oil_temp = 122 | new OneWireTemperature(dts, 1000, "/Oil Temperature/oneWire"); 123 | 124 | oil_temp->connect_to(new Linear(1.0, 0.0, "/Oil Temperature/linear")) 125 | ->connect_to( 126 | new SKOutputFloat("propulsion.engine.oilTemperature", 127 | "/Oil Temperature/sk_path")); 128 | 129 | // Aft Cabin temp - /environment/inside/aftCabin/temperature 130 | auto* aft_cabin_temp = 131 | new OneWireTemperature(dts, 1000, "/Aft Cabin Temperature/oneWire"); 132 | 133 | aft_cabin_temp->connect_to(new Linear(1.0, 0.0, "/Aft Cabin Temperature/linear")) 134 | ->connect_to( 135 | new SKOutputFloat("environment.inside.aftCabin.temperature", 136 | "/Aft Cabin Temperature/sk_path")); 137 | 138 | // Exhaust Elbow Temp sensor - /propulsion/engine/intakeManifoldTemperature 139 | auto* elbow_temp = 140 | new OneWireTemperature(dts, 1000, "/Elbow Temperature/oneWire"); 141 | 142 | elbow_temp->connect_to(new Linear(1.0, 0.0, "/Elbow Temperature/linear")) 143 | ->connect_to( 144 | new SKOutputFloat("propulsion.engine.intakeManifoldTemperature", 145 | "/Elbow Temperature/sk_path")); 146 | 147 | // Exhaust barrel sensor - /propulsion/engine/exhaustTemperature 148 | auto* exhaust_temp = 149 | new OneWireTemperature(dts, 1000, "/Exhaust Temperature/oneWire"); 150 | 151 | exhaust_temp->connect_to(new Linear(1.0, 0.0, "/Exhaust Temperature/linear")) 152 | ->connect_to( 153 | new SKOutputFloat("propulsion.engine.exhaustTemperature", 154 | "/Exhaust Temperature/sk_path")); 155 | 156 | // Alternator Temperature - /electrical/alternator/temperature 157 | auto* alternator_temp = 158 | new OneWireTemperature(dts, 1000, "/Alternator Temperature/oneWire"); 159 | 160 | alternator_temp->connect_to(new Linear(1.0, 0.0, "/Alternator Temperature/linear")) 161 | ->connect_to( 162 | new SKOutputFloat("electrical.alternator.temperature", 163 | "/Alternator Temperature/sk_path")); 164 | 165 | //RPM Application///// 166 | 167 | const char* config_path_calibrate = "/Engine RPM/calibrate"; 168 | const char* config_path_skpath = "/Engine RPM/sk_path"; 169 | const float multiplier = 1.0; 170 | 171 | auto* sensor = new DigitalInputCounter(16, INPUT_PULLUP, RISING, 500); 172 | 173 | sensor->connect_to(new Frequency(multiplier, config_path_calibrate)) 174 | // connect the output of sensor to the input of Frequency() 175 | ->connect_to(new SKOutputFloat("propulsion.engine.revolutions", config_path_skpath)); 176 | // connect the output of Frequency() to a Signal K Output as a number 177 | 178 | sensor->connect_to(new Frequency(6)) 179 | // times by 6 to go from Hz to RPM 180 | ->connect_to(new FuelInterpreter("/Engine Fuel/curve")) 181 | ->connect_to(new SKOutputFloat("propulsion.engine.fuel.rate", "/Engine Fuel/sk_path")); 182 | 183 | 184 | /// BME280 SENSOR CODE - Temp/Humidity/Altitude/Pressure Sensor //// 185 | 186 | // 0x77 is the default address. Some chips use 0x76, which is shown here. 187 | // If you need to use the TwoWire library instead of the Wire library, there 188 | // is a different constructor: see bmp280.h 189 | 190 | bme280.begin(); 191 | // Create a RepeatSensor with float output that reads the temperature 192 | // using the function defined above. 193 | auto* bme280_temp = 194 | new RepeatSensor(5000, read_temp_callback); 195 | 196 | auto* bme280_pressure = 197 | new RepeatSensor(60000, read_pressure_callback); 198 | 199 | auto* bme280_humidity = 200 | new RepeatSensor(60000, read_humidity_callback); 201 | 202 | // Send the temperature to the Signal K server as a Float 203 | bme280_temp->connect_to(new SKOutputFloat("environment.inside.engineBay.temperature")); 204 | 205 | bme280_pressure->connect_to(new SKOutputFloat("environment.inside.engineBay.pressure")); 206 | 207 | bme280_humidity->connect_to(new SKOutputFloat("environment.inside.engineBay.relativeHumidity")); 208 | 209 | //// Pressure Sender Config //// 210 | 211 | const float Vin = 3.45; 212 | const float R1 = 47.0; 213 | auto* analog_input = new AnalogInput(36, 500); //- Pin 36 is Analogue 0 214 | 215 | analog_input->connect_to(new AnalogVoltage(Vin,Vin)) 216 | ->connect_to(new VoltageDividerR2(R1, Vin, "/Engine Pressure/sender")) 217 | ->connect_to(new PressureInterpreter("/Engine Pressure/curve")) 218 | ->connect_to(new Linear(1.0, 0.0, "/Engine Pressure/calibrate")) 219 | ->connect_to(new SKOutputFloat("propulsion.engine.oilPressure", "/Engine Pressure/sk_path")); 220 | 221 | //// Bilge Monitor ///// 222 | auto* bilge = new DigitalInputState(13, INPUT_PULLUP, 5000); //- Pin 13 is Digital 7 223 | 224 | auto int_to_string_function = [](int input) ->String { 225 | if (input == 1) { 226 | return "Water present!"; 227 | } 228 | else { // input == 0 229 | return "bilge clear"; 230 | } 231 | }; 232 | 233 | auto int_to_string_transform = new LambdaTransform(int_to_string_function); 234 | 235 | bilge->connect_to(int_to_string_transform) 236 | ->connect_to(new SKOutputString("propulsion.engine.bilge")); 237 | 238 | bilge->connect_to(new SKOutputFloat("propulsion.engine.bilge.raw")); 239 | 240 | // Start networking, SK server connections and other SensESP internals 241 | sensesp_app->start(); 242 | 243 | } 244 | 245 | void loop() { app.tick(); } -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Please visit documentation for the other options and examples 4 | ; https://docs.platformio.org/page/projectconf.html 5 | 6 | [platformio] 7 | ;set default_envs to whichever board(s) you use. Build/Run/etc processes those envs 8 | default_envs = 9 | esp32dev 10 | 11 | [env] 12 | ; Global data for all [env:***] 13 | framework = arduino 14 | lib_ldf_mode = deep 15 | monitor_speed = 115200 16 | lib_deps = 17 | ; Peg the SensESP version to 2.0.0 and compatible versions 18 | SignalK/SensESP @ ^2.0.0 19 | adafruit/Adafruit BMP280 Library@^2.6.3 20 | SensESP/OneWire@^2.0.0 21 | adafruit/Adafruit BME280 Library@^2.2.2 22 | Wire 23 | 24 | ; Add any additional dependencies here 25 | 26 | [espressif32_base] 27 | ;this section has config items common to all ESP32 boards 28 | platform = espressif32 29 | build_unflags = 30 | -Werror=reorder 31 | board_build.partitions = min_spiffs.csv 32 | monitor_filters = esp32_exception_decoder 33 | 34 | [env:esp32dev] 35 | extends = espressif32_base 36 | board = esp32dev 37 | build_flags = 38 | -D LED_BUILTIN=2 39 | ; Uncomment the following to disable debug output altogether 40 | ;-D DEBUG_DISABLED 41 | ; Uncomment the following to enable the remote debug telnet interface on port 23 42 | ;-D REMOTE_DEBUG 43 | 44 | ;; Uncomment and change these if PlatformIO can't auto-detect the ports 45 | ;upload_port = /dev/tty.SLAB_USBtoUART 46 | ;monitor_port = /dev/tty.SLAB_USBtoUART 47 | 48 | ;; Uncomment the following lines to use Over-the-air (OTA) Updates 49 | ;upload_protocol = espota 50 | ;upload_port = IP_ADDRESS_OF_ESP_HERE 51 | ;upload_flags = 52 | ; --auth=YOUR_OTA_PASSWORD 53 | --------------------------------------------------------------------------------