From 52ff24e6499f3daf90012f83d9d8621ae5b2bc65 Mon Sep 17 00:00:00 2001 From: Johnathon Slightham Date: Sat, 7 Feb 2026 01:38:05 -0500 Subject: [PATCH] Display control code --- .../constants/include/constants/module.h | 11 +- components/flatbuffers/CMakeLists.txt | 2 +- .../flatbuffers/SensorMessageBuilder.cpp | 7 +- .../flatbuffers/TextControlMessageBuilder.cpp | 11 + .../include/SensorMessageBuilder.h | 8 +- .../include/TextControlMessageBuilder.h | 18 ++ .../RobotModule_generated.h | 48 +++- .../SensorMessage_generated.h | 213 ++++++++++++------ .../TextControlMessage_generated.h | 104 +++++++++ components/oled/CMakeLists.txt | 3 + components/oled/Oled.cpp | 159 +++++++++++++ components/oled/include/Oled.h | 23 ++ components/oled/include/font8x8_basic.h | 163 ++++++++++++++ components/oled/include/ssd1306.h | 45 ++++ main/CMakeLists.txt | 2 +- main/control/ActuatorFactory.cpp | 3 + main/control/DCMotorActuator.cpp | 2 + main/control/OledActuator.cpp | 33 +++ main/control/Servo1Actuator.cpp | 2 + main/include/control/OledActuator.h | 24 ++ pinout.md | 2 + 21 files changed, 797 insertions(+), 86 deletions(-) create mode 100644 components/flatbuffers/TextControlMessageBuilder.cpp create mode 100644 components/flatbuffers/include/TextControlMessageBuilder.h create mode 100644 components/flatbuffers/include/flatbuffers_generated/TextControlMessage_generated.h create mode 100644 components/oled/CMakeLists.txt create mode 100644 components/oled/Oled.cpp create mode 100644 components/oled/include/Oled.h create mode 100644 components/oled/include/font8x8_basic.h create mode 100644 components/oled/include/ssd1306.h create mode 100644 main/control/OledActuator.cpp create mode 100644 main/include/control/OledActuator.h diff --git a/components/constants/include/constants/module.h b/components/constants/include/constants/module.h index 394ae92..e0f5b82 100644 --- a/components/constants/include/constants/module.h +++ b/components/constants/include/constants/module.h @@ -9,7 +9,13 @@ #include "flatbuffers_generated/RobotModule_generated.h" -inline std::unordered_map MODULE_TO_NUM_CHANNELS_MAP {{ModuleType_SPLITTER, 4}, {ModuleType_SERVO_1, 2}, {ModuleType_DC_MOTOR, 1}, {ModuleType_SERVO_2, 2}}; +inline std::unordered_map MODULE_TO_NUM_CHANNELS_MAP { + {ModuleType_SPLITTER, 4}, + {ModuleType_SERVO_1, 2}, + {ModuleType_DC_MOTOR, 1}, + {ModuleType_SERVO_2, 2}, + {ModuleType_DISPLAY, 1} +}; #define PC_ADDR 0 @@ -22,6 +28,9 @@ inline std::unordered_map MODULE_TO_NUM_CHANNELS_MAP {{ModuleType_SPLI #define DC_ENCODER_A 15 #define DC_ENCODER_B 16 +#define OLED_SDA 17 +#define OLED_SCL 18 + inline std::unordered_map CHANNEL_TO_0_DEG_MAP{{0, 9} }; inline std::unordered_map CHANNEL_TO_90_DEG_MAP{{0, 7} }; inline std::unordered_map CHANNEL_TO_180_DEG_MAP{{0, 8} }; diff --git a/components/flatbuffers/CMakeLists.txt b/components/flatbuffers/CMakeLists.txt index a7b031f..b576d3f 100644 --- a/components/flatbuffers/CMakeLists.txt +++ b/components/flatbuffers/CMakeLists.txt @@ -1,2 +1,2 @@ -idf_component_register(SRCS "MPIMessageBuilder.cpp" "AngleControlMessageBuilder.cpp" "TopologyMessageBuilder.cpp" "SensorMessageBuilder.cpp" +idf_component_register(SRCS "MPIMessageBuilder.cpp" "AngleControlMessageBuilder.cpp" "TopologyMessageBuilder.cpp" "SensorMessageBuilder.cpp" "TextControlMessageBuilder.cpp" INCLUDE_DIRS "include") diff --git a/components/flatbuffers/SensorMessageBuilder.cpp b/components/flatbuffers/SensorMessageBuilder.cpp index f40d687..a9e4f66 100644 --- a/components/flatbuffers/SensorMessageBuilder.cpp +++ b/components/flatbuffers/SensorMessageBuilder.cpp @@ -26,8 +26,13 @@ SerializedMessage SensorMessageBuilder::build_sensor_message(std::vector(buffer); + } +} diff --git a/components/flatbuffers/include/SensorMessageBuilder.h b/components/flatbuffers/include/SensorMessageBuilder.h index 7cf3cef..782ff55 100644 --- a/components/flatbuffers/include/SensorMessageBuilder.h +++ b/components/flatbuffers/include/SensorMessageBuilder.h @@ -15,11 +15,15 @@ struct current_angle { int16_t angle; }; -typedef std::variant sensor_value; +struct target_text { + std::string text; +}; + +typedef std::variant sensor_value; class SensorMessageBuilder { public: - SensorMessageBuilder() : builder_(128) { + SensorMessageBuilder() : builder_(256) { } SerializedMessage build_sensor_message(std::vector &values); diff --git a/components/flatbuffers/include/TextControlMessageBuilder.h b/components/flatbuffers/include/TextControlMessageBuilder.h new file mode 100644 index 0000000..1ce18d7 --- /dev/null +++ b/components/flatbuffers/include/TextControlMessageBuilder.h @@ -0,0 +1,18 @@ +// +// Created by Johnathon Slightham on 2025-06-30. +// + +#ifndef TEXTCONTROLMESSAGEBUILDER_H_ +#define TEXTCONTROLMESSAGEBUILDER_H_ + +#include "flatbuffers_generated/TextControlMessage_generated.h" +#include "flatbuffers/flatbuffers.h" + +namespace Flatbuffers { + class TextControlMessageBuilder { + public: + static const Messaging::TextControlMessage* parse_text_control_message(const uint8_t* buffer); + }; +} + +#endif diff --git a/components/flatbuffers/include/flatbuffers_generated/RobotModule_generated.h b/components/flatbuffers/include/flatbuffers_generated/RobotModule_generated.h index 087e271..58d10a9 100644 --- a/components/flatbuffers/include/flatbuffers_generated/RobotModule_generated.h +++ b/components/flatbuffers/include/flatbuffers_generated/RobotModule_generated.h @@ -7,7 +7,7 @@ #include "flatbuffers/flatbuffers.h" // Ensure the included flatbuffers.h is the same version as when this file was -// // generated, otherwise it may not be compatible. +// generated, otherwise it may not be compatible. // static_assert(FLATBUFFERS_VERSION_MAJOR == 25 && // FLATBUFFERS_VERSION_MINOR == 2 && // FLATBUFFERS_VERSION_REVISION == 10, @@ -25,35 +25,71 @@ enum ModuleType : int8_t { ModuleType_DC_MOTOR = 2, ModuleType_BATTERY = 3, ModuleType_SERVO_2 = 4, + ModuleType_DISPLAY = 5, + ModuleType_GRIPPER = 6, + ModuleType_SPEAKER = 7, + ModuleType_IMU = 8, + ModuleType_DISTANCE_SENSOR = 9, + ModuleType_SPLITTER_2 = 10, + ModuleType_SPLITTER_3 = 11, + ModuleType_SPLITTER_4 = 12, + ModuleType_SPLITTER_5 = 13, + ModuleType_SPLITTER_6 = 14, + ModuleType_SPLITTER_7 = 15, + ModuleType_SPLITTER_8 = 16, ModuleType_MIN = ModuleType_SPLITTER, - ModuleType_MAX = ModuleType_SERVO_2 + ModuleType_MAX = ModuleType_SPLITTER_8 }; -inline const ModuleType (&EnumValuesModuleType())[5] { +inline const ModuleType (&EnumValuesModuleType())[17] { static const ModuleType values[] = { ModuleType_SPLITTER, ModuleType_SERVO_1, ModuleType_DC_MOTOR, ModuleType_BATTERY, - ModuleType_SERVO_2 + ModuleType_SERVO_2, + ModuleType_DISPLAY, + ModuleType_GRIPPER, + ModuleType_SPEAKER, + ModuleType_IMU, + ModuleType_DISTANCE_SENSOR, + ModuleType_SPLITTER_2, + ModuleType_SPLITTER_3, + ModuleType_SPLITTER_4, + ModuleType_SPLITTER_5, + ModuleType_SPLITTER_6, + ModuleType_SPLITTER_7, + ModuleType_SPLITTER_8 }; return values; } inline const char * const *EnumNamesModuleType() { - static const char * const names[6] = { + static const char * const names[18] = { "SPLITTER", "SERVO_1", "DC_MOTOR", "BATTERY", "SERVO_2", + "DISPLAY", + "GRIPPER", + "SPEAKER", + "IMU", + "DISTANCE_SENSOR", + "SPLITTER_2", + "SPLITTER_3", + "SPLITTER_4", + "SPLITTER_5", + "SPLITTER_6", + "SPLITTER_7", + "SPLITTER_8", nullptr }; return names; } inline const char *EnumNameModuleType(ModuleType e) { - if (::flatbuffers::IsOutRange(e, ModuleType_SPLITTER, ModuleType_SERVO_2)) return ""; + if (::flatbuffers::IsOutRange(e, ModuleType_SPLITTER, ModuleType_SPLITTER_8)) return ""; const size_t index = static_cast(e); return EnumNamesModuleType()[index]; } diff --git a/components/flatbuffers/include/flatbuffers_generated/SensorMessage_generated.h b/components/flatbuffers/include/flatbuffers_generated/SensorMessage_generated.h index 455fa23..be795f5 100644 --- a/components/flatbuffers/include/flatbuffers_generated/SensorMessage_generated.h +++ b/components/flatbuffers/include/flatbuffers_generated/SensorMessage_generated.h @@ -1,5 +1,6 @@ // automatically generated by the FlatBuffers compiler, do not modify + #ifndef FLATBUFFERS_GENERATED_SENSORMESSAGE_MESSAGING_H_ #define FLATBUFFERS_GENERATED_SENSORMESSAGE_MESSAGING_H_ @@ -17,6 +18,9 @@ namespace Messaging { struct TargetAngle; struct TargetAngleBuilder; +struct CurrentText; +struct CurrentTextBuilder; + struct CurrentAngle; struct CurrentAngleBuilder; @@ -27,57 +31,69 @@ enum SensorValue : uint8_t { SensorValue_NONE = 0, SensorValue_TargetAngle = 1, SensorValue_CurrentAngle = 2, + SensorValue_CurrentText = 3, SensorValue_MIN = SensorValue_NONE, - SensorValue_MAX = SensorValue_CurrentAngle + SensorValue_MAX = SensorValue_CurrentText }; -inline const SensorValue (&EnumValuesSensorValue())[3] { +inline const SensorValue (&EnumValuesSensorValue())[4] { static const SensorValue values[] = { - SensorValue_NONE, SensorValue_TargetAngle, SensorValue_CurrentAngle}; + SensorValue_NONE, + SensorValue_TargetAngle, + SensorValue_CurrentAngle, + SensorValue_CurrentText + }; return values; } -inline const char *const *EnumNamesSensorValue() { - static const char *const names[4] = {"NONE", "TargetAngle", "CurrentAngle", - nullptr}; +inline const char * const *EnumNamesSensorValue() { + static const char * const names[5] = { + "NONE", + "TargetAngle", + "CurrentAngle", + "CurrentText", + nullptr + }; return names; } inline const char *EnumNameSensorValue(SensorValue e) { - if (::flatbuffers::IsOutRange(e, SensorValue_NONE, SensorValue_CurrentAngle)) - return ""; + if (::flatbuffers::IsOutRange(e, SensorValue_NONE, SensorValue_CurrentText)) return ""; const size_t index = static_cast(e); return EnumNamesSensorValue()[index]; } -template struct SensorValueTraits { +template struct SensorValueTraits { static const SensorValue enum_value = SensorValue_NONE; }; -template <> struct SensorValueTraits { +template<> struct SensorValueTraits { static const SensorValue enum_value = SensorValue_TargetAngle; }; -template <> struct SensorValueTraits { +template<> struct SensorValueTraits { static const SensorValue enum_value = SensorValue_CurrentAngle; }; -bool VerifySensorValue(::flatbuffers::Verifier &verifier, const void *obj, - SensorValue type); -bool VerifySensorValueVector( - ::flatbuffers::Verifier &verifier, - const ::flatbuffers::Vector<::flatbuffers::Offset> *values, - const ::flatbuffers::Vector *types); +template<> struct SensorValueTraits { + static const SensorValue enum_value = SensorValue_CurrentText; +}; + +bool VerifySensorValue(::flatbuffers::Verifier &verifier, const void *obj, SensorValue type); +bool VerifySensorValueVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types); struct TargetAngle FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef TargetAngleBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_VALUE = 4 }; - int16_t value() const { return GetField(VT_VALUE, 0); } + int16_t value() const { + return GetField(VT_VALUE, 0); + } bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && - VerifyField(verifier, VT_VALUE, 2) && verifier.EndTable(); + VerifyField(verifier, VT_VALUE, 2) && + verifier.EndTable(); } }; @@ -89,7 +105,7 @@ struct TargetAngleBuilder { fbb_.AddElement(TargetAngle::VT_VALUE, value, 0); } explicit TargetAngleBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { + : fbb_(_fbb) { start_ = fbb_.StartTable(); } ::flatbuffers::Offset Finish() { @@ -99,22 +115,77 @@ struct TargetAngleBuilder { } }; -inline ::flatbuffers::Offset -CreateTargetAngle(::flatbuffers::FlatBufferBuilder &_fbb, int16_t value = 0) { +inline ::flatbuffers::Offset CreateTargetAngle( + ::flatbuffers::FlatBufferBuilder &_fbb, + int16_t value = 0) { TargetAngleBuilder builder_(_fbb); builder_.add_value(value); return builder_.Finish(); } +struct CurrentText FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef CurrentTextBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_VALUE = 4 + }; + const ::flatbuffers::String *value() const { + return GetPointer(VT_VALUE); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_VALUE) && + verifier.VerifyString(value()) && + verifier.EndTable(); + } +}; + +struct CurrentTextBuilder { + typedef CurrentText Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_value(::flatbuffers::Offset<::flatbuffers::String> value) { + fbb_.AddOffset(CurrentText::VT_VALUE, value); + } + explicit CurrentTextBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateCurrentText( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> value = 0) { + CurrentTextBuilder builder_(_fbb); + builder_.add_value(value); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateCurrentTextDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const char *value = nullptr) { + auto value__ = value ? _fbb.CreateString(value) : 0; + return Messaging::CreateCurrentText( + _fbb, + value__); +} + struct CurrentAngle FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef CurrentAngleBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_VALUE = 4 }; - int16_t value() const { return GetField(VT_VALUE, 0); } + int16_t value() const { + return GetField(VT_VALUE, 0); + } bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && - VerifyField(verifier, VT_VALUE, 2) && verifier.EndTable(); + VerifyField(verifier, VT_VALUE, 2) && + verifier.EndTable(); } }; @@ -126,7 +197,7 @@ struct CurrentAngleBuilder { fbb_.AddElement(CurrentAngle::VT_VALUE, value, 0); } explicit CurrentAngleBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { + : fbb_(_fbb) { start_ = fbb_.StartTable(); } ::flatbuffers::Offset Finish() { @@ -136,8 +207,9 @@ struct CurrentAngleBuilder { } }; -inline ::flatbuffers::Offset -CreateCurrentAngle(::flatbuffers::FlatBufferBuilder &_fbb, int16_t value = 0) { +inline ::flatbuffers::Offset CreateCurrentAngle( + ::flatbuffers::FlatBufferBuilder &_fbb, + int16_t value = 0) { CurrentAngleBuilder builder_(_fbb); builder_.add_value(value); return builder_.Finish(); @@ -153,8 +225,7 @@ struct SensorMessage FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { return GetPointer *>(VT_VALUES_TYPE); } const ::flatbuffers::Vector<::flatbuffers::Offset> *values() const { - return GetPointer< - const ::flatbuffers::Vector<::flatbuffers::Offset> *>(VT_VALUES); + return GetPointer> *>(VT_VALUES); } bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && @@ -171,17 +242,14 @@ struct SensorMessageBuilder { typedef SensorMessage Table; ::flatbuffers::FlatBufferBuilder &fbb_; ::flatbuffers::uoffset_t start_; - void add_values_type( - ::flatbuffers::Offset<::flatbuffers::Vector> values_type) { + void add_values_type(::flatbuffers::Offset<::flatbuffers::Vector> values_type) { fbb_.AddOffset(SensorMessage::VT_VALUES_TYPE, values_type); } - void add_values( - ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> - values) { + void add_values(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> values) { fbb_.AddOffset(SensorMessage::VT_VALUES, values); } explicit SensorMessageBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { + : fbb_(_fbb) { start_ = fbb_.StartTable(); } ::flatbuffers::Offset Finish() { @@ -194,8 +262,7 @@ struct SensorMessageBuilder { inline ::flatbuffers::Offset CreateSensorMessage( ::flatbuffers::FlatBufferBuilder &_fbb, ::flatbuffers::Offset<::flatbuffers::Vector> values_type = 0, - ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> - values = 0) { + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> values = 0) { SensorMessageBuilder builder_(_fbb); builder_.add_values(values); builder_.add_values_type(values_type); @@ -206,43 +273,41 @@ inline ::flatbuffers::Offset CreateSensorMessageDirect( ::flatbuffers::FlatBufferBuilder &_fbb, const std::vector *values_type = nullptr, const std::vector<::flatbuffers::Offset> *values = nullptr) { - auto values_type__ = - values_type ? _fbb.CreateVector(*values_type) : 0; - auto values__ = - values ? _fbb.CreateVector<::flatbuffers::Offset>(*values) : 0; - return Messaging::CreateSensorMessage(_fbb, values_type__, values__); + auto values_type__ = values_type ? _fbb.CreateVector(*values_type) : 0; + auto values__ = values ? _fbb.CreateVector<::flatbuffers::Offset>(*values) : 0; + return Messaging::CreateSensorMessage( + _fbb, + values_type__, + values__); } -inline bool VerifySensorValue(::flatbuffers::Verifier &verifier, - const void *obj, SensorValue type) { +inline bool VerifySensorValue(::flatbuffers::Verifier &verifier, const void *obj, SensorValue type) { switch (type) { - case SensorValue_NONE: { - return true; - } - case SensorValue_TargetAngle: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case SensorValue_CurrentAngle: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - default: - return true; + case SensorValue_NONE: { + return true; + } + case SensorValue_TargetAngle: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case SensorValue_CurrentAngle: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case SensorValue_CurrentText: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + default: return true; } } -inline bool VerifySensorValueVector( - ::flatbuffers::Verifier &verifier, - const ::flatbuffers::Vector<::flatbuffers::Offset> *values, - const ::flatbuffers::Vector *types) { - if (!values || !types) - return !values && !types; - if (values->size() != types->size()) - return false; +inline bool VerifySensorValueVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types) { + if (!values || !types) return !values && !types; + if (values->size() != types->size()) return false; for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) { - if (!VerifySensorValue(verifier, values->Get(i), - types->GetEnum(i))) { + if (!VerifySensorValue( + verifier, values->Get(i), types->GetEnum(i))) { return false; } } @@ -253,17 +318,17 @@ inline const Messaging::SensorMessage *GetSensorMessage(const void *buf) { return ::flatbuffers::GetRoot(buf); } -inline const Messaging::SensorMessage * -GetSizePrefixedSensorMessage(const void *buf) { +inline const Messaging::SensorMessage *GetSizePrefixedSensorMessage(const void *buf) { return ::flatbuffers::GetSizePrefixedRoot(buf); } -inline bool VerifySensorMessageBuffer(::flatbuffers::Verifier &verifier) { +inline bool VerifySensorMessageBuffer( + ::flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer(nullptr); } -inline bool -VerifySizePrefixedSensorMessageBuffer(::flatbuffers::Verifier &verifier) { +inline bool VerifySizePrefixedSensorMessageBuffer( + ::flatbuffers::Verifier &verifier) { return verifier.VerifySizePrefixedBuffer(nullptr); } @@ -279,6 +344,6 @@ inline void FinishSizePrefixedSensorMessageBuffer( fbb.FinishSizePrefixed(root); } -} // namespace Messaging +} // namespace Messaging -#endif // FLATBUFFERS_GENERATED_SENSORMESSAGE_MESSAGING_H_ +#endif // FLATBUFFERS_GENERATED_SENSORMESSAGE_MESSAGING_H_ diff --git a/components/flatbuffers/include/flatbuffers_generated/TextControlMessage_generated.h b/components/flatbuffers/include/flatbuffers_generated/TextControlMessage_generated.h new file mode 100644 index 0000000..8412a1b --- /dev/null +++ b/components/flatbuffers/include/flatbuffers_generated/TextControlMessage_generated.h @@ -0,0 +1,104 @@ +// automatically generated by the FlatBuffers compiler, do not modify + + +#ifndef FLATBUFFERS_GENERATED_TEXTCONTROLMESSAGE_MESSAGING_H_ +#define FLATBUFFERS_GENERATED_TEXTCONTROLMESSAGE_MESSAGING_H_ + +#include "flatbuffers/flatbuffers.h" + +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +// static_assert(FLATBUFFERS_VERSION_MAJOR == 25 && +// FLATBUFFERS_VERSION_MINOR == 2 && +// FLATBUFFERS_VERSION_REVISION == 10, +// "Non-compatible flatbuffers version included"); + +namespace Messaging { + +struct TextControlMessage; +struct TextControlMessageBuilder; + +struct TextControlMessage FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef TextControlMessageBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_MESSAGE = 4 + }; + const ::flatbuffers::String *message() const { + return GetPointer(VT_MESSAGE); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_MESSAGE) && + verifier.VerifyString(message()) && + verifier.EndTable(); + } +}; + +struct TextControlMessageBuilder { + typedef TextControlMessage Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_message(::flatbuffers::Offset<::flatbuffers::String> message) { + fbb_.AddOffset(TextControlMessage::VT_MESSAGE, message); + } + explicit TextControlMessageBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateTextControlMessage( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> message = 0) { + TextControlMessageBuilder builder_(_fbb); + builder_.add_message(message); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateTextControlMessageDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const char *message = nullptr) { + auto message__ = message ? _fbb.CreateString(message) : 0; + return Messaging::CreateTextControlMessage( + _fbb, + message__); +} + +inline const Messaging::TextControlMessage *GetTextControlMessage(const void *buf) { + return ::flatbuffers::GetRoot(buf); +} + +inline const Messaging::TextControlMessage *GetSizePrefixedTextControlMessage(const void *buf) { + return ::flatbuffers::GetSizePrefixedRoot(buf); +} + +inline bool VerifyTextControlMessageBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer(nullptr); +} + +inline bool VerifySizePrefixedTextControlMessageBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer(nullptr); +} + +inline void FinishTextControlMessageBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.Finish(root); +} + +inline void FinishSizePrefixedTextControlMessageBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.FinishSizePrefixed(root); +} + +} // namespace Messaging + +#endif // FLATBUFFERS_GENERATED_TEXTCONTROLMESSAGE_MESSAGING_H_ diff --git a/components/oled/CMakeLists.txt b/components/oled/CMakeLists.txt new file mode 100644 index 0000000..7a59207 --- /dev/null +++ b/components/oled/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(INCLUDE_DIRS "include" + PRIV_REQUIRES driver + SRCS "Oled.cpp") diff --git a/components/oled/Oled.cpp b/components/oled/Oled.cpp new file mode 100644 index 0000000..7c1f26e --- /dev/null +++ b/components/oled/Oled.cpp @@ -0,0 +1,159 @@ + +#include + +#include "driver/gpio.h" +#include "driver/i2c.h" +#include "esp_err.h" +#include "esp_log.h" +#include "Oled.h" +#include "font8x8_basic.h" +#include "ssd1306.h" + +#define TAG "OLED" + +// 128x64 pixels +// 8 pixels per byte +// 128 bytes per page -> ((128*64)/8)/128 = 8 pages. +#define DISPLAY_WIDTH 128 +#define DISPLAY_HEIGHT 64 +#define PAGE_SIZE 128 +#define PAGE_COUNT 8 +#define I2C_PORT I2C_NUM_0 + +void Oled::init(uint8_t sda_pin, uint8_t scl_pin) { + i2c_config_t i2c_config = { + .mode = I2C_MODE_MASTER, + .sda_io_num = sda_pin, + .scl_io_num = scl_pin, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master = { + .clk_speed = 1000000 + }, + .clk_flags = 0, + }; + i2c_param_config(I2C_PORT, &i2c_config); + + i2c_driver_install(I2C_PORT, I2C_MODE_MASTER, 0, 0, 0); + + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_CHARGE_PUMP, true); + i2c_master_write_byte(cmd, 0x14, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_SEGMENT_REMAP, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_COM_SCAN_MODE, true); + i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_ON, true); + i2c_master_stop(cmd); + + if (ESP_OK != i2c_master_cmd_begin(I2C_PORT, cmd, 10 / portTICK_PERIOD_MS)) { + ESP_LOGE(TAG, "configuration failed"); + } + + i2c_cmd_link_delete(cmd); + + clear_display(); +} + +void Oled::clear_display() { + ESP_LOGI(TAG, "clear_display"); + i2c_cmd_handle_t cmd; + + uint8_t page[PAGE_SIZE] = {0}; + for (uint8_t i = 0; i < PAGE_COUNT; i++) { + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_SINGLE, true); + i2c_master_write_byte(cmd, 0xB0 | i, true); + + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_DATA_STREAM, true); + i2c_master_write(cmd, page, PAGE_SIZE, true); + i2c_master_stop(cmd); + i2c_master_cmd_begin(I2C_PORT, cmd, 10 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + } +} + +void Oled::set_contrast(uint8_t contrast) { + i2c_cmd_handle_t cmd; + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_CONTRAST, true); + i2c_master_write_byte(cmd, contrast, true); + i2c_master_stop(cmd); + i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); +} + +void Oled::display_text(const std::string& text) { + ESP_LOGI(TAG, "display_text %s", text.c_str()); + i2c_cmd_handle_t cmd; + + uint8_t cur_page = 0; + uint8_t col = 0; + auto col_width = DISPLAY_WIDTH / 8; // using an 8x8 font + auto num_rows = DISPLAY_HEIGHT / 8; // using an 8x8 font + auto max_size = col_width * num_rows; + + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); + i2c_master_write_byte(cmd, 0x00, true); // reset column + i2c_master_write_byte(cmd, 0x10, true); + i2c_master_write_byte(cmd, 0xB0 | cur_page, true); // reset page + + i2c_master_stop(cmd); + i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + + for (uint8_t i = 0; i < text.size() && i < max_size; i++) { + ESP_LOGI(TAG, "%d: %c", i, text[i]); + if (text[i] == '\n' || col >= col_width) { + ESP_LOGI(TAG, "new line"); + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); + i2c_master_write_byte(cmd, 0x00, true); // reset column + i2c_master_write_byte(cmd, 0x10, true); + i2c_master_write_byte(cmd, 0xB0 | ++cur_page, true); // increment page + + i2c_master_stop(cmd); + i2c_master_cmd_begin(I2C_NUM_0, cmd, 10/portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + col = 0; + } else { + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_DATA_STREAM, true); + uint8_t rotated[8]; + rotate8x8((uint8_t*)font8x8_basic[(uint8_t)text[i]], rotated); + i2c_master_write(cmd, rotated, 8, true); + + i2c_master_stop(cmd); + i2c_master_cmd_begin(I2C_NUM_0, cmd, 10/portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + col++; + } + } +} + +void Oled::rotate8x8(uint8_t in[8], uint8_t out[8]) { + for (int x = 0; x < 8; x++) { + out[x] = 0; + for (int y = 0; y < 8; y++) { + if (in[y] & (1 << x)) { + out[x] |= (1 << (7 - y)); + } + } + } +} diff --git a/components/oled/include/Oled.h b/components/oled/include/Oled.h new file mode 100644 index 0000000..854b812 --- /dev/null +++ b/components/oled/include/Oled.h @@ -0,0 +1,23 @@ + +#include +#include + +#ifndef OLED_H +#define OLED_H + +class Oled { +public: + Oled(uint8_t sda_pin, uint8_t scl_pin) { + init(sda_pin, scl_pin); + }; + void clear_display(); + void set_contrast(uint8_t contrast); + void display_text(const std::string& text); + +private: + void init(uint8_t sda_pin, uint8_t scl_pin); + void rotate8x8(uint8_t in[8], uint8_t out[8]); + +}; + +#endif // OLED_H diff --git a/components/oled/include/font8x8_basic.h b/components/oled/include/font8x8_basic.h new file mode 100644 index 0000000..a2038a0 --- /dev/null +++ b/components/oled/include/font8x8_basic.h @@ -0,0 +1,163 @@ +#ifndef FONT_H +#define FONT_H + +#include + +// Adapted from https://github.com/dhepper/font8x8/blob/master/font8x8_basic.h + +/** + * 8x8 monochrome bitmap fonts for rendering + * Author: Daniel Hepper + * + * License: Public Domain + * + * Based on: + * // Summary: font8x8.h + * // 8x8 monochrome bitmap fonts for rendering + * // + * // Author: + * // Marcel Sondaar + * // International Business Machines (public domain VGA fonts) + * // + * // License: + * // Public Domain + * + * Fetched from: http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm + **/ + +// Constant: font8x8_basic +// Contains an 8x8 font map for unicode points U+0000 - U+007F (basic latin) +uint8_t font8x8_basic[128][8] = { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x18, 0x00, 0x18, 0x18, 0x3C, 0x3C, 0x18 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36 }, + { 0x00, 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36 }, + { 0x00, 0x0C, 0x1F, 0x30, 0x1E, 0x03, 0x3E, 0x0C }, + { 0x00, 0x63, 0x66, 0x0C, 0x18, 0x33, 0x63, 0x00 }, + { 0x00, 0x6E, 0x33, 0x3B, 0x6E, 0x1C, 0x36, 0x1C }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x06 }, + { 0x00, 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18 }, + { 0x00, 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06 }, + { 0x00, 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00 }, + { 0x00, 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00 }, + { 0x06, 0x0C, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00 }, + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x01, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60 }, + { 0x00, 0x3E, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x3E }, + { 0x00, 0x3F, 0x0C, 0x0C, 0x0C, 0x0C, 0x0E, 0x0C }, + { 0x00, 0x3F, 0x33, 0x06, 0x1C, 0x30, 0x33, 0x1E }, + { 0x00, 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E }, + { 0x00, 0x78, 0x30, 0x7F, 0x33, 0x36, 0x3C, 0x38 }, + { 0x00, 0x1E, 0x33, 0x30, 0x30, 0x1F, 0x03, 0x3F }, + { 0x00, 0x1E, 0x33, 0x33, 0x1F, 0x03, 0x06, 0x1C }, + { 0x00, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x33, 0x3F }, + { 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E }, + { 0x00, 0x0E, 0x18, 0x30, 0x3E, 0x33, 0x33, 0x1E }, + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00 }, + { 0x06, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00 }, + { 0x00, 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18 }, + { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00 }, + { 0x00, 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06 }, + { 0x00, 0x0C, 0x00, 0x0C, 0x18, 0x30, 0x33, 0x1E }, + { 0x00, 0x1E, 0x03, 0x7B, 0x7B, 0x7B, 0x63, 0x3E }, + { 0x00, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x1E, 0x0C }, + { 0x00, 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F }, + { 0x00, 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C }, + { 0x00, 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F }, + { 0x00, 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F }, + { 0x00, 0x0F, 0x06, 0x16, 0x1E, 0x16, 0x46, 0x7F }, + { 0x00, 0x7C, 0x66, 0x73, 0x03, 0x03, 0x66, 0x3C }, + { 0x00, 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33 }, + { 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E }, + { 0x00, 0x1E, 0x33, 0x33, 0x30, 0x30, 0x30, 0x78 }, + { 0x00, 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67 }, + { 0x00, 0x7F, 0x66, 0x46, 0x06, 0x06, 0x06, 0x0F }, + { 0x00, 0x63, 0x63, 0x6B, 0x7F, 0x7F, 0x77, 0x63 }, + { 0x00, 0x63, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x63 }, + + { 0x00, 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C }, + { 0x00, 0x0F, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3F }, + { 0x00, 0x38, 0x1E, 0x3B, 0x33, 0x33, 0x33, 0x1E }, + { 0x00, 0x67, 0x66, 0x36, 0x3E, 0x66, 0x66, 0x3F }, + { 0x00, 0x1E, 0x33, 0x38, 0x0E, 0x07, 0x33, 0x1E }, + { 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x2D, 0x3F }, + { 0x00, 0x3F, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33 }, + { 0x00, 0x0C, 0x1E, 0x33, 0x33, 0x33, 0x33, 0x33 }, + { 0x00, 0x63, 0x77, 0x7F, 0x6B, 0x63, 0x63, 0x63 }, + { 0x00, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x63 }, + { 0x00, 0x1E, 0x0C, 0x0C, 0x1E, 0x33, 0x33, 0x33 }, + { 0x00, 0x7F, 0x66, 0x4C, 0x18, 0x31, 0x63, 0x7F }, + { 0x00, 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E }, + { 0x00, 0x40, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03 }, + { 0x00, 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E }, + { 0x00, 0x00, 0x00, 0x00, 0x63, 0x36, 0x1C, 0x08 }, + { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0C, 0x0C }, + { 0x00, 0x6E, 0x33, 0x3E, 0x30, 0x1E, 0x00, 0x00 }, + { 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x07 }, + { 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00, 0x00 }, + { 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x30, 0x38 }, + { 0x00, 0x1E, 0x03, 0x3F, 0x33, 0x1E, 0x00, 0x00 }, + { 0x00, 0x0F, 0x06, 0x06, 0x0F, 0x06, 0x36, 0x1C }, + { 0x1F, 0x30, 0x3E, 0x33, 0x33, 0x6E, 0x00, 0x00 }, + { 0x00, 0x67, 0x66, 0x66, 0x6E, 0x36, 0x06, 0x07 }, + { 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0E, 0x00, 0x0C }, + { 0x1E, 0x33, 0x33, 0x30, 0x30, 0x30, 0x00, 0x30 }, + { 0x00, 0x67, 0x36, 0x1E, 0x36, 0x66, 0x06, 0x07 }, + { 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0E }, + { 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x33, 0x00, 0x00 }, + { 0x00, 0x33, 0x33, 0x33, 0x33, 0x1F, 0x00, 0x00 }, + { 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00, 0x00 }, + { 0x0F, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00, 0x00 }, + { 0x78, 0x30, 0x3E, 0x33, 0x33, 0x6E, 0x00, 0x00 }, + { 0x00, 0x0F, 0x06, 0x66, 0x6E, 0x3B, 0x00, 0x00 }, + { 0x00, 0x1F, 0x30, 0x1E, 0x03, 0x3E, 0x00, 0x00 }, + { 0x00, 0x18, 0x2C, 0x0C, 0x0C, 0x3E, 0x0C, 0x08 }, + { 0x00, 0x6E, 0x33, 0x33, 0x33, 0x33, 0x00, 0x00 }, + { 0x00, 0x0C, 0x1E, 0x33, 0x33, 0x33, 0x00, 0x00 }, + { 0x00, 0x36, 0x7F, 0x7F, 0x6B, 0x63, 0x00, 0x00 }, + { 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00, 0x00 }, + { 0x1F, 0x30, 0x3E, 0x33, 0x33, 0x33, 0x00, 0x00 }, + { 0x00, 0x3F, 0x26, 0x0C, 0x19, 0x3F, 0x00, 0x00 }, + { 0x00, 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38 }, + { 0x00, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18 }, + { 0x00, 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x6E }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } +}; + + +#endif // FONT_H diff --git a/components/oled/include/ssd1306.h b/components/oled/include/ssd1306.h new file mode 100644 index 0000000..66b1387 --- /dev/null +++ b/components/oled/include/ssd1306.h @@ -0,0 +1,45 @@ +#ifndef SSD_1306_H +#define SSD_1306_H + +// From http://robotcantalk.blogspot.com/2015/03/interfacing-arduino-with-ssd1306-driven.html + +// SLA (0x3C) + WRITE_MODE (0x00) = 0x78 (0b01111000) +#define OLED_I2C_ADDRESS 0x3C + +// Control byte +#define OLED_CONTROL_BYTE_CMD_SINGLE 0x80 +#define OLED_CONTROL_BYTE_CMD_STREAM 0x00 +#define OLED_CONTROL_BYTE_DATA_STREAM 0x40 + +// Fundamental commands (pg.28) +#define OLED_CMD_SET_CONTRAST 0x81 // follow with 0x7F +#define OLED_CMD_DISPLAY_RAM 0xA4 +#define OLED_CMD_DISPLAY_ALLON 0xA5 +#define OLED_CMD_DISPLAY_NORMAL 0xA6 +#define OLED_CMD_DISPLAY_INVERTED 0xA7 +#define OLED_CMD_DISPLAY_OFF 0xAE +#define OLED_CMD_DISPLAY_ON 0xAF + +// Addressing Command Table (pg.30) +#define OLED_CMD_SET_MEMORY_ADDR_MODE 0x20 // follow with 0x00 = HORZ mode = Behave like a KS108 graphic LCD +#define OLED_CMD_SET_COLUMN_RANGE 0x21 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x7F = COL127 +#define OLED_CMD_SET_PAGE_RANGE 0x22 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x07 = PAGE7 + +// Hardware Config (pg.31) +#define OLED_CMD_SET_DISPLAY_START_LINE 0x40 +#define OLED_CMD_SET_SEGMENT_REMAP 0xA1 +#define OLED_CMD_SET_MUX_RATIO 0xA8 // follow with 0x3F = 64 MUX +#define OLED_CMD_SET_COM_SCAN_MODE 0xC8 +#define OLED_CMD_SET_DISPLAY_OFFSET 0xD3 // follow with 0x00 +#define OLED_CMD_SET_COM_PIN_MAP 0xDA // follow with 0x12 +#define OLED_CMD_NOP 0xE3 // NOP + +// Timing and Driving Scheme (pg.32) +#define OLED_CMD_SET_DISPLAY_CLK_DIV 0xD5 // follow with 0x80 +#define OLED_CMD_SET_PRECHARGE 0xD9 // follow with 0xF1 +#define OLED_CMD_SET_VCOMH_DESELCT 0xDB // follow with 0x30 + +// Charge Pump (pg.62) +#define OLED_CMD_SET_CHARGE_PUMP 0x8D // follow with 0x14 + +#endif // SSD_1306_H diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 260c604..641b250 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -17,7 +17,7 @@ else() endif() idf_component_register(SRCS ${ALL_SRCS} - PRIV_REQUIRES esp_psram spi_flash nvs_flash esp_event rpc constants config rmt esp_driver_gptimer dataLink flatbuffers esp_driver_ledc + PRIV_REQUIRES esp_psram spi_flash nvs_flash esp_event rpc constants config rmt esp_driver_gptimer dataLink flatbuffers esp_driver_ledc oled INCLUDE_DIRS "include") if(DEFINED SRC_BOARD AND SRC_BOARD) diff --git a/main/control/ActuatorFactory.cpp b/main/control/ActuatorFactory.cpp index d715aea..d5182c9 100644 --- a/main/control/ActuatorFactory.cpp +++ b/main/control/ActuatorFactory.cpp @@ -7,6 +7,7 @@ #include "control/ActuatorFactory.h" #include "control/DCMotorActuator.h" #include "control/Servo1Actuator.h" +#include "control/OledActuator.h" #include "flatbuffers_generated/RobotModule_generated.h" std::unique_ptr @@ -18,6 +19,8 @@ ActuatorFactory::create_actuator(const ModuleType type) { return std::make_unique(); case ModuleType_DC_MOTOR: return std::make_unique(); + case ModuleType_DISPLAY: + return std::make_unique(); default: return nullptr; } diff --git a/main/control/DCMotorActuator.cpp b/main/control/DCMotorActuator.cpp index 0195468..7dc5970 100644 --- a/main/control/DCMotorActuator.cpp +++ b/main/control/DCMotorActuator.cpp @@ -101,6 +101,8 @@ void DCMotorActuator::setup_encoder() { } void DCMotorActuator::actuate(uint8_t *cmd) { + // todo: Do we want to verify flatbuffers here? + // Will introduce latency, and means that we need to also send the size to the actuate function. const auto *angleControlCmd = Flatbuffers::AngleControlMessageBuilder::parse_angle_control_message(cmd); this->m_target_angle = angleControlCmd->angle(); diff --git a/main/control/OledActuator.cpp b/main/control/OledActuator.cpp new file mode 100644 index 0000000..86e0d0b --- /dev/null +++ b/main/control/OledActuator.cpp @@ -0,0 +1,33 @@ +// +// Created by Johnathon Slightham on 2025-07-15. +// + +#include "control/OledActuator.h" +#include "TextControlMessageBuilder.h" +#include "SensorMessageBuilder.h" +#include "constants/module.h" +#include "driver/ledc.h" +#include "flatbuffers_generated/SensorMessage_generated.h" +#include "util/number_utils.h" +#include "esp_log.h" + +void OledActuator::actuate(uint8_t *cmd) { + // todo: Do we want to verify flatbuffers here? + // Will introduce latency, and means that we need to also send the size to the actuate function. + + const auto *text_control_message = Flatbuffers::TextControlMessageBuilder::parse_text_control_message(cmd); + const auto new_str = text_control_message->message()->str(); + + if (m_display_str != new_str) { + m_display_str = new_str; + + m_oled->clear_display(); + if (m_display_str != "") { + m_oled->display_text(m_display_str); + } + } +} + +std::vector OledActuator::get_sensor_data() { + return {Flatbuffers::target_text{m_display_str}}; +} diff --git a/main/control/Servo1Actuator.cpp b/main/control/Servo1Actuator.cpp index 9024c73..70cd500 100644 --- a/main/control/Servo1Actuator.cpp +++ b/main/control/Servo1Actuator.cpp @@ -39,6 +39,8 @@ Servo1Actuator::Servo1Actuator() { } void Servo1Actuator::actuate(uint8_t *cmd) { + // todo: Do we want to verify flatbuffers here? + // Will introduce latency, and means that we need to also send the size to the actuate function. const auto *angleControlCmd = Flatbuffers::AngleControlMessageBuilder::parse_angle_control_message(cmd); const auto newDuty = diff --git a/main/include/control/OledActuator.h b/main/include/control/OledActuator.h new file mode 100644 index 0000000..9447f09 --- /dev/null +++ b/main/include/control/OledActuator.h @@ -0,0 +1,24 @@ +// Oled module + +#ifndef OLED_ACTUATOR_H +#define OLED_ACTUATOR_H + +#include "IActuator.h" +#include "ISensor.h" +#include "Oled.h" +#include "constants/module.h" +#include + +class OledActuator final : public IActuator { + public: + OledActuator() : m_oled(std::make_unique(OLED_SDA, OLED_SCL)) {}; + ~OledActuator() override = default; + void actuate(std::uint8_t *cmd) override; + std::vector get_sensor_data() override; + + private: + std::string m_display_str = ""; + std::unique_ptr m_oled; +}; + +#endif //OLED_ACTUATOR_H diff --git a/pinout.md b/pinout.md index 53b23f3..e232aba 100644 --- a/pinout.md +++ b/pinout.md @@ -18,3 +18,5 @@ | 14 | Ch. 3 RMT RX | | 15 | DC Motor Encoder A | | 16 | DC Motor Encoder B | +| 17 | OLED SDA | +| 18 | OLED SCL |