SignalK-Orientation 1.0.0
Orientation output in Signal K format for ESP32
Loading...
Searching...
No Matches
example_main.cpp
1
21#include <memory>
22
26#include "sensesp.h"
27#include "sensesp/sensors/sensor.h"
28#include "sensesp/signalk/signalk_output.h"
29#include "sensesp/system/lambda_consumer.h"
30#include "sensesp_app_builder.h"
31
36#include "sensesp/sensors/digital_input.h"
37#include "sensesp/transforms/debounce.h"
38
46#include "sensesp/transforms/angle_correction.h"
47#include "sensesp/transforms/curveinterpolator.h"
48
53#include "sensesp/signalk/signalk_types.h"
54
59#include "sensesp/transforms/linear.h"
60
64#include "orientation_sensor.h"
65#include "signalk_orientation.h"
66
67
68// Sensor hardware details: I2C addresses and pins
69#define BOARD_ACCEL_MAG_I2C_ADDR (0x1F)
70#define BOARD_GYRO_I2C_ADDR (0x21)
71#define PIN_I2C_SDA (23) // Adjust to your board. A value of -1
72#define PIN_I2C_SCL (25) // will use default Arduino pins.
73#define PIN_SWITCH_CAL_SAVE (32) // Optional switch attached to this pin saves magnetic calibration
74#define SWITCH_ACTIVE_STATE (0) // Input is LOW when Switch is pushed
75
76// How often orientation parameters are published via Signal K message
77// If a report interval is saved for a particular sensor path (via the web
78// interface), that overrides the following #define for that report.
79#define ORIENTATION_REPORTING_INTERVAL_MS (100)
80
81using namespace sensesp;
82
87class DeviationInterpolator : public CurveInterpolator {
88 public:
89 DeviationInterpolator(String config_path = "")
90 : CurveInterpolator( NULL, config_path) {
104 //default Deviation table is straight 1:1 conversion
105 // clear_samples();
106 // add_sample(CurveInterpolator::Sample(0.0, 0.0));
107 // add_sample(CurveInterpolator::Sample(6.3, 6.3));
108 }
109};
110
111
114void setup() {
115 //without following, get runtime messages:
116 // I (2147) ARDUINO: LEDC attached to pin 2 (channel 0, resolution 8)
117 // E (2149) ARDUINO: IO 0 is not set as GPIO. Execute digitalMode(0, OUTPUT) first.
118 // E (2149) ARDUINO: IO 2 is not set as GPIO. Execute digitalMode(2, OUTPUT) first.
119 // E (2149) ARDUINO: IO 4 is not set as GPIO. Execute digitalMode(4, OUTPUT) first.
120 pinMode(0, OUTPUT);
121 pinMode(2, OUTPUT); //this doesn't prevent the GPIO2 complaint.
122 pinMode(4, OUTPUT);
123
124 SetupLogging(ESP_LOG_INFO); //inits the Serial port and starts logging output
125
133 SensESPAppBuilder builder;
134 sensesp_app = (&builder)
135 // Set a custom hostname for the app.
136 ->set_hostname("eCompass")
137 // Optionally, hard-code the WiFi and Signal K server
138 // settings. They can also be configured using the web interface.
139 //->set_wifi_client("My WiFi SSID", "my_wifi_password")
140 //->set_wifi_access_point("My AP SSID", "my_ap_password")
141 //->set_sk_server("10.0.0.27", 3000)
142 //SensESP has several builtin sensors, e.g. freemem, uptime, IP address
143 // Optionally enable them here to output their values in SK reports.
144 ->enable_uptime_sensor()
145 ->enable_ip_address_sensor()
146 ->enable_free_mem_sensor()
147 ->enable_system_hz_sensor()
148 //->enable_wifi_signal_sensor()
149 ->get_app();
150
173 const char* kSKPathHeadingCompass = "navigation.headingCompass";
174 const char* kSKPathHeadingMagnetic = "navigation.headingMagnetic";
175 const char* kSKPathAttitude = "navigation.attitude";
182 const char* kSKPathTurnRate = "navigation.rateOfTurn";
183 const char* kSKPathRollRate = "navigation.rateOfRoll";
184 const char* kSKPathPitchRate = "navigation.rateOfPitch";
189 const char* kSKPathTemperature =
190 "environment.inside.ecompass.temperature";
194 const char* kSKPathAccelX = "sensors.accelerometer.accel_x";
195 const char* kSKPathAccelY = "sensors.accelerometer.accel_y";
196 const char* kSKPathAccelZ = "sensors.accelerometer.accel_z";
207 const char* kSKPathMagFit = "orientation.calibration.magfit";
208 const char* kSKPathMagFitTrial = "orientation.calibration.magfittrial";
209 const char* kSKPathMagSolver = "orientation.calibration.magsolver";
210 const char* kSKPathMagInclination = "orientation.calibration.maginclination";
211 const char* kSKPathMagBValue = "orientation.calibration.magmagnitude";
212 const char* kSKPathMagBValueTrial = "orientation.calibration.magmagnitudetrial";
213 const char* kSKPathMagNoise = "orientation.calibration.magnoise";
214 const char* kSKPathMagCalValues = "orientation.calibration.magvalues";
215
236 const char* kConfigPathNone = "";
237
255 auto* orientation_sensor = new OrientationSensor(
256 PIN_I2C_SDA, PIN_I2C_SCL, BOARD_ACCEL_MAG_I2C_ADDR, BOARD_GYRO_I2C_ADDR);
257 const int fusionIntervalMs = 1000 / orientation_sensor->GetFusionRateHz();
258 event_loop()->onRepeat( fusionIntervalMs,
259 [orientation_sensor]() { orientation_sensor->ReadAndProcessSensors(); }
260 );
300 // Create sensor for compass heading output
301 auto* sensor_heading = new OrientationValues(
302 orientation_sensor, OrientationValues::kCompassHeading);
303 auto compass_heading = std::make_shared<RepeatSensor<float>>(
304 ORIENTATION_REPORTING_INTERVAL_MS,
305 [sensor_heading]() { return sensor_heading->ReportValue(); }
306 );
307 /* RepeatSensor() is not defined as a configurable class, so we can't
308 use the web UI to update the reporting rate. TODO - expand RepeatSensor
309 class definition.
310 ConfigItem(compass_heading)
311 ->set_title("Heading Report Rate")
312 ->set_description("interval (ms) between Compass and Magnetic Heading outputs")
313 ->set_sort_order(200);
314 */
315 auto compass_sk_output = std::make_shared<SKOutput<float>>(
316 kSKPathHeadingCompass, // Signal K path
317 kConfigPathNone // configuration path
318 );
319
325 auto magneticheading_sk_output = std::make_shared<SKOutput<float>>(
326 kSKPathHeadingMagnetic, // Signal K path
327 kConfigPathNone // configuration path
328 // metadata
329 );
339 const char* kConfigPathDeviation = "/sensors/hdg/deviation";
340 auto* deviationInterpolator = new DeviationInterpolator(kConfigPathDeviation);
341 ConfigItem(deviationInterpolator)
342 ->set_title("Deviation Table")
343 ->set_description("Interpolation Values")
344 ->set_sort_order(1001);
345
346 //Add an AngleCorrection transform, to adjust for any mounting offsets
347 // Pi/2 rotation in this example
348 const char* kConfigPathHeadingOffset = "/sensors/hdg/offset";
349 auto* mountingOffset = new AngleCorrection((PI/2.0), 0.0, kConfigPathHeadingOffset);
350 ConfigItem(mountingOffset)
351 ->set_title("Mounting Offset")
352 ->set_description("Enter any adjustment to be applied to all headings (e.g. from mounting offsets)")
353 ->set_sort_order(400);
354
355 // Connect compass output to Signal K transforms and outputs
356 compass_heading
357 ->connect_to(mountingOffset)
358 ->connect_to(compass_sk_output)
359 ->connect_to(deviationInterpolator)
360 ->connect_to(new AngleCorrection(0.0, 0.0, kConfigPathNone)) // Normalize to [0..2Pi] after interpolation
361 ->connect_to(magneticheading_sk_output);
362
367/*
368 auto* sensor_roll = new OrientationValues(
369 orientation_sensor, OrientationValues::kRoll);
370 auto* sensor_pitch = new OrientationValues(
371 orientation_sensor, OrientationValues::kPitch);
372 auto* sensor_yaw = new OrientationValues(
373 orientation_sensor, OrientationValues::kYaw);
374
375 auto attitude_sensor = std::make_shared<RepeatSensor<AttitudeVector>>(
376 ORIENTATION_REPORTING_INTERVAL_MS,
377 [sensor_roll, sensor_pitch, sensor_yaw]()
378 { return AttitudeVector(sensor_roll->ReportValue(),
379 sensor_pitch->ReportValue(),
380 sensor_yaw->ReportValue()
381 );
382 }
383 );
384 auto attitude_sk_output = std::make_shared<SKOutput<AttitudeVector>>(
385 kSKPathAttitude, // Signal K path
386 kConfigPathNone // configuration path
387 );
388 attitude_sensor->connect_to(attitude_sk_output);
389*/
413/*
414 // Create output for Magnetic Calibration Fit
415 auto* sensor_magcalfit = new OrientationValues(
416 orientation_sensor, OrientationValues::kMagCalFitInUse);
417 auto magcalfit = std::make_shared<RepeatSensor<float>>(
418 4000, //update output value every 4000ms
419 [sensor_magcalfit]() { return sensor_magcalfit->ReportValue(); });
420 // Need to provide metadata, as Mag Cal related values are not defined in the Signal K spec.
421 auto magcalfit_metadata = std::make_shared<SKMetadata>();
422 magcalfit_metadata->units_ = "%";
423 magcalfit_metadata->description_ = "Goodness-of-fit of readings using current Magnetic Calibration";
424 magcalfit_metadata->display_name_ = "Magnetic Calibration Fit";
425 magcalfit_metadata->short_name_ = "MagCalFit";
426 auto magcalfit_output = std::make_shared<SKOutput<float>>(
427 kSKPathMagFit, // Signal K path
428 kConfigPathNone, // configuration path
429 magcalfit_metadata
430 );
431 magcalfit->connect_to(magcalfit_output);
432
433 // Create output for Magnetic Calibration Fit Trial
434 auto* sensor_magcal_candidate = new OrientationValues(
435 orientation_sensor, OrientationValues::kMagCalFitTrial);
436 auto magcaltrial = std::make_shared<RepeatSensor<float>>(
437 4000, //update output value every 4000ms
438 [sensor_magcal_candidate]() { return sensor_magcal_candidate->ReportValue(); });
439 auto magcaltrial_metadata = std::make_shared<SKMetadata>();
440 magcaltrial_metadata->units_ = "%";
441 magcaltrial_metadata->description_ = "Goodness-of-fit of readings using trial Magnetic Calibration";
442 magcaltrial_metadata->display_name_ = "Magnetic Calibration Fit Trial";
443 magcaltrial_metadata->short_name_ = "MagCalFitTrial";
444 auto magcaltrial_output = std::make_shared<SKOutput<float>>(
445 kSKPathMagFitTrial, // Signal K path
446 kConfigPathNone, // configuration path
447 magcaltrial_metadata
448 );
449 magcaltrial->connect_to(magcaltrial_output);
450
451 // Create output for kSKPathMagSolver
452 auto* sensor_cal_order = new OrientationValues(
453 orientation_sensor, OrientationValues::kMagCalAlgorithmSolver);
454 auto magcalorder = std::make_shared<RepeatSensor<float>>(
455 4000, //update output value every 4000ms
456 [sensor_cal_order]() { return sensor_cal_order->ReportValue(); });
457 auto cal_solver_metadata = std::make_shared<SKMetadata>();
458 cal_solver_metadata->units_ = "[0,4,7,10]";
459 cal_solver_metadata->description_ = "Order of calibration algorithm used [0,4,7,10] 10 is best.";
460 cal_solver_metadata->display_name_ = "Magnetic Calibration Algorithm Order";
461 cal_solver_metadata->short_name_ = "MagCalOrder";
462 auto cal_solver_output = std::make_shared<SKOutput<int>>(
463 kSKPathMagSolver, // Signal K path
464 "", // configuration path
465 cal_solver_metadata
466 );
467 magcalorder->connect_to(cal_solver_output);
468
469 // Create output for Magnetic Inclination
470 auto* sensor_mag_inclination = new OrientationValues(
471 orientation_sensor, OrientationValues::kMagInclination);
472 auto maginclination = std::make_shared<RepeatSensor<float>>(
473 4000, //update output value every 4000ms
474 [sensor_mag_inclination]() { return sensor_mag_inclination->ReportValue(); });
475 auto inclination_metadata = std::make_shared<SKMetadata>();
476 inclination_metadata->units_ = "rad";
477 inclination_metadata->description_ = "Magnetic field inclination from horizontal";
478 inclination_metadata->display_name_ = "Magnetic Inclination";
479 inclination_metadata->short_name_ = "MagInclination";
480 auto inclination_output = std::make_shared<SKOutput<float>>(
481 kSKPathMagInclination, // Signal K path
482 "", // configuration path
483 inclination_metadata
484 );
485 maginclination->connect_to(inclination_output);
486
487 // Create output for Magnetic B Field Strength
488 auto* sensor_mag_b_value = new OrientationValues(
489 orientation_sensor, OrientationValues::kMagFieldMagnitude);
490 auto magbvalue = std::make_shared<RepeatSensor<float>>(
491 4000, //update output value every 4000ms
492 [sensor_mag_b_value]() { return sensor_mag_b_value->ReportValue(); });
493 auto magbvalue_metadata = std::make_shared<SKMetadata>();
494 magbvalue_metadata->units_ = "uT";
495 magbvalue_metadata->description_ = "Magnetic field strength using current calibration";
496 magbvalue_metadata->display_name_ = "Magnetic B Field";
497 magbvalue_metadata->short_name_ = "B Field";
498 auto magbvalue_output = std::make_shared<SKOutput<float>>(
499 kSKPathMagBValue, // Signal K path
500 "", // configuration path
501 magbvalue_metadata
502 );
503 magbvalue->connect_to(magbvalue_output);
504
505 // Create output for Magnetic B Field Strength using Trial Calibration
506 auto* sensor_mag_b_trial_value = new OrientationValues(
507 orientation_sensor, OrientationValues::kMagFieldMagnitudeTrial);
508 auto magbtrialvalue = std::make_shared<RepeatSensor<float>>(
509 4000, //update output value every 4000ms
510 [sensor_mag_b_trial_value]() { return sensor_mag_b_trial_value->ReportValue(); });
511 auto magbtrialvalue_metadata = std::make_shared<SKMetadata>();
512 magbtrialvalue_metadata->units_ = "uT";
513 magbtrialvalue_metadata->description_ = "Magnetic field strength using trial calibration";
514 magbtrialvalue_metadata->display_name_ = "Magnetic B Field Trial";
515 magbtrialvalue_metadata->short_name_ = "B Field Trial";
516 auto magbtrialvalue_output = std::make_shared<SKOutput<float>>(
517 kSKPathMagBValueTrial, // Signal K path
518 "", // configuration path
519 magbtrialvalue_metadata
520 );
521 magbtrialvalue->connect_to(magbtrialvalue_output);
522
523 // Create output for Magnetic Noise
524 auto* sensor_mag_noise = new OrientationValues(
525 orientation_sensor, OrientationValues::kMagNoiseCovariance);
526 auto magnoise = std::make_shared<RepeatSensor<float>>(
527 4000, //update output value every 4000ms
528 [sensor_mag_noise]() { return sensor_mag_noise->ReportValue(); });
529 auto mag_noise_metadata = std::make_shared<SKMetadata>();
530 mag_noise_metadata->units_ = "unitless";
531 mag_noise_metadata->description_ = "Magnetic Noise / Interference";
532 mag_noise_metadata->display_name_ = "Magnetic Noise";
533 mag_noise_metadata->short_name_ = "Mag Noise";
534 auto mag_noise_output = std::make_shared<SKOutput<float>>(
535 kSKPathMagNoise, // Signal K path
536 "", // configuration path
537 mag_noise_metadata
538 );
539 magnoise->connect_to(mag_noise_output);
540*/
541
544/*
545 // Create output for Roll Rate
546 auto* sensor_roll_rate = new OrientationValues(
547 orientation_sensor, OrientationValues::kRateOfRoll);
548 auto roll_rate = std::make_shared<RepeatSensor<float>>(
549 200, //update output value every 200ms
550 [sensor_roll_rate]() { return sensor_roll_rate->ReportValue(); });
551 auto metadata_roll_rate = std::make_shared<SKMetadata>();
552 metadata_roll_rate->units_ = "rad/s";
553 metadata_roll_rate->description_ = "Rate of Roll about bow-stern axis";
554 metadata_roll_rate->display_name_ = "Roll Rate";
555 metadata_roll_rate->short_name_ = "Roll Rate";
556 auto roll_rate_output = std::make_shared<SKOutput<float>>(
557 kSKPathRollRate, // Signal K path
558 "", // configuration path
559 metadata_roll_rate
560 );
561 roll_rate->connect_to(roll_rate_output);
562
563
564 // Create output for Pitch Rate
565 auto* sensor_pitch_rate = new OrientationValues(
566 orientation_sensor, OrientationValues::kRateOfPitch);
567 auto pitch_rate = std::make_shared<RepeatSensor<float>>(
568 200, //update output value every 200ms
569 [sensor_pitch_rate]() { return sensor_pitch_rate->ReportValue(); });
570 auto metadata_pitch_rate = std::make_shared<SKMetadata>();
571 metadata_pitch_rate->units_ = "rad/s";
572 metadata_pitch_rate->description_ = "Rate of Pitch about port-starboard axis";
573 metadata_pitch_rate->display_name_ = "Pitch Rate";
574 metadata_pitch_rate->short_name_ = "Pitch Rate";
575 auto pitch_rate_output = std::make_shared<SKOutput<float>>(
576 kSKPathPitchRate, // Signal K path
577 "", // configuration path
578 metadata_pitch_rate
579 );
580 pitch_rate->connect_to(pitch_rate_output);
581
582 // Create output for Turn Rate
583 auto* sensor_turn_rate = new OrientationValues(
584 orientation_sensor, OrientationValues::kRateOfTurn);
585 auto turn_rate = std::make_shared<RepeatSensor<float>>(
586 200, //update output value every 200ms
587 [sensor_turn_rate]() { return sensor_turn_rate->ReportValue(); });
588 auto metadata_turn_rate = std::make_shared<SKMetadata>();
589 metadata_turn_rate->units_ = "rad/s";
590 metadata_turn_rate->description_ = "Rate of Turn about mast-keel axis";
591 metadata_turn_rate->display_name_ = "Turn Rate";
592 metadata_turn_rate->short_name_ = "Turn Rate";
593 auto turn_rate_output = std::make_shared<SKOutput<float>>(
594 kSKPathTurnRate, // Signal K path
595 "", // configuration path
596 metadata_turn_rate
597 );
598 turn_rate->connect_to(turn_rate_output);
599*/
600
619/*
620 // Create output for X Acceleration
621 auto* sensor_accel_X = new OrientationValues(
622 orientation_sensor, OrientationValues::kAccelerationX);
623 auto accel_X = std::make_shared<RepeatSensor<float>>(
624 1000, //update output value every 1000ms
625 [sensor_accel_X]() { return sensor_accel_X->ReportValue(); });
626 auto metadata_accel_X = std::make_shared<SKMetadata>();
627 metadata_accel_X->units_ = "m/s^2";
628 metadata_accel_X->description_ = "Acceleration in X axis of eCompass";
629 metadata_accel_X->display_name_ = "X Axis Acceleration";
630 metadata_accel_X->short_name_ = "Accel X";
631 auto accel_X_output = std::make_shared<SKOutput<float>>(
632 kSKPathAccelX, // Signal K path
633 "", // configuration path
634 metadata_accel_X
635 );
636 accel_X->connect_to(accel_X_output);
637*/
638
642/* auto* sensor_temperature = new OrientationValues(
643 orientation_sensor, OrientationValues::kTemperature);
644
645 auto temperature = std::make_shared<RepeatSensor<float>>(
646 1000, //update output value every 1000ms
647 [sensor_temperature]() { return sensor_temperature->ReportValue(); });
648
649 // Temperature readings are passed through a linear transform
650 // to allow for calibration/linearization via web interface. Other
651 // transforms are available. Ensure you #include the appropriate file(s).
652 const char* kConfigPathTemperatureCal = "/sensors/temp/calibrate";
653 auto temperatureCal = std::make_shared<Linear>(1.0, 0.0,kConfigPathTemperatureCal);
654 ConfigItem(temperatureCal)
655 ->set_title("Compass Temperature Calibration")
656 ->set_description("Calibration / Linearization of temperature reported by eCompass IC")
657 ->set_sort_order(206);
658
659 // Create output, with metadata to indicate source and location of temp data
660 auto temperature_metadata = std::make_shared<SKMetadata>();
661 temperature_metadata->units_ = "K";
662 temperature_metadata->description_ = "Temperature reported by orientation sensor";
663 temperature_metadata->display_name_ = "eCompass Temperature";
664 temperature_metadata->short_name_ = "Comp. T";
665
666 auto temperature_output = std::make_shared<SKOutput<float>>(
667 kSKPathTemperature, // Signal K path
668 kConfigPathNone, // configuration path
669 temperature_metadata
670 );
671
672 temperature
673 ->connect_to(temperatureCal)
674 ->connect_to(temperature_output);
675*/
685/* auto* button_watcher = new DigitalInputChange(
686 PIN_SWITCH_CAL_SAVE, INPUT_PULLUP, CHANGE, kConfigPathNone);
687 // Create a debounce transform
688 const int kDebounceDelay = 350; // only react to pushes >350 ms
689 const char* kConfigPathDebounceSwitch = "/debounce/delay";
690 auto* debounce = new DebounceInt(kDebounceDelay, kConfigPathDebounceSwitch);
691 ConfigItem(debounce)
692 ->set_title("MagCal Button Debounce")
693 ->set_description("Debounce delay (ms) for Magnetic Calibration save button.")
694 ->set_sort_order(1000);
695 // Define the action taken when button is active and debounce has elapsed.
696 // Provide it with the context of orientation_sensor so it can access save fcn.
697 auto save_mcal_function = [orientation_sensor](int input) {
698 if (input == SWITCH_ACTIVE_STATE) {
699 orientation_sensor->sensor_interface_->SaveMagneticCalibration();
700 debugI("Magnetic Calibration values saved");
701 }
702 };
703 auto* button_consumer = new LambdaConsumer<int>(save_mcal_function);
704 // Connect the button -> debounce -> save magnetic calibration fcn
705 button_watcher->connect_to(debounce)->connect_to(button_consumer);
706*/
707
708 // To avoid garbage collecting all shared pointers created in setup(),
709 // loop from here.
710 while (true) {
711 loop();
712 }
713}
714
715void loop() { event_loop()->tick(); }
DeviationInterpolator(String config_path="")
OrientationSensor represents a 9-Degrees-of-Freedom sensor (magnetometer, accelerometer,...
OrientationValues reads and outputs orientation parameters.
@ kCompassHeading
compass heading, also called yaw
Orientation sensor interface to SensESP.
Vessel orientation data structures definition file.