diff --git a/CustomDevices/_template/MFCustomDevice.cpp b/CustomDevices/_template/MFCustomDevice.cpp new file mode 100644 index 00000000..0569e5d3 --- /dev/null +++ b/CustomDevices/_template/MFCustomDevice.cpp @@ -0,0 +1,230 @@ +#include "MFCustomDevice.h" +#include "commandmessenger.h" +#include "allocateMem.h" +#include "MyCustomClass.h" +#include "MFEEPROM.h" +extern MFEEPROM MFeeprom; + +/* ********************************************************************************** + The custom device pins, type and configuration is stored in the EEPROM + While loading the config the adresses in the EEPROM are transferred to the constructor + Within the constructor you have to copy the EEPROM content to a buffer + and evaluate him. The buffer is used for all 3 types (pins, type configuration), + so do it step by step. + The max size of the buffer is defined here. It must be the size of the + expected max length of these strings. + + E.g. 6 pins are required, each pin could have two characters (two digits), + each pins are delimited by "|" and the string is NULL terminated. + -> (6 * 2) + 5 + 1 = 18 bytes is the maximum. + The custom type is "MyCustomClass", which means 14 characters plus NULL = 15 + The configuration is "myConfig", which means 8 characters plus NULL = 9 + The maximum characters to be expected is 18, so MEMLEN_STRING_BUFFER has to be at least 18 +********************************************************************************** */ +#define MEMLEN_STRING_BUFFER 40 + +// reads a string from EEPROM at given address which is '.' terminated and saves it to the buffer +bool MFCustomDevice::getStringFromEEPROM(uint16_t addreeprom, char *buffer) +{ + char temp = 0; + uint8_t counter = 0; + do { + temp = MFeeprom.read_byte((addreeprom)++); // read the first character + buffer[counter++] = temp; // save character and locate next buffer position + if (counter >= MEMLEN_STRING_BUFFER) { // nameBuffer will be exceeded + return false; // abort copying to buffer + } + } while (temp != '.'); // reads until limiter '.' and locates the next free buffer position + buffer[counter - 1] = 0x00; // replace '.' by NULL, terminates the string + return true; +} + +/* ********************************************************************************** + Within the connector pins, a device name and a config string can be defined + These informations are stored in the EEPROM like for the other devices. + While reading the config from the EEPROM this function is called. + It is the first function which will be called for the custom device. + If it fits into the memory buffer, the constructor for the customer device + will be called +********************************************************************************** */ + +MFCustomDevice::MFCustomDevice(uint16_t adrPin, uint16_t adrType, uint16_t adrConfig) +{ + _initialized = true; + /* ********************************************************************************** + Do something which is required to setup your custom device + ********************************************************************************** */ + + char *params, *p = NULL; + char parameter[MEMLEN_STRING_BUFFER]; + uint8_t _pin1, _pin2, _pin3; + + /* ********************************************************************************** + read the Type from the EEPROM, copy it into a buffer and evaluate it + it's only required if your custom device handles multiple devices with + different contructors + the string get's NOT stored as this would need a lot of RAM, instead a variable + is used to store the type + ********************************************************************************** */ + getStringFromEEPROM(adrType, parameter); + if (strcmp(parameter, MY_CUSTOM_TYPE_1) == 0) + _customType = MY_CUSTOM_DEVICE_1; + if (strcmp(parameter, MY_CUSTOM_TYPE_2) == 0) + _customType = MY_CUSTOM_DEVICE_2; + + /* ********************************************************************************** + Next call the constructor of your custom device + adapt it to the needs of your constructor + if you have multiple classes, check for _customType which constructor + has to be called (e.g. if (_customType == MY_CUSTOM_DEVICE_1) ....) + ********************************************************************************** */ + if (_customType == 1) { + if (!FitInMemory(sizeof(MyCustomClass))) { + // Error Message to Connector + cmdMessenger.sendCmd(kStatus, F("Custom Device does not fit in Memory")); + return; + } + /* ********************************************************************************************** + read the pins from the EEPROM, copy them into a buffer + ********************************************************************************************** */ + getStringFromEEPROM(adrPin, parameter); + /* ********************************************************************************************** + split the pins up into single pins. As the number of pins could be different between + multiple devices, it is done here. + ********************************************************************************************** */ + params = strtok_r(parameter, "|", &p); + _pin1 = atoi(params); + params = strtok_r(NULL, "|", &p); + _pin2 = atoi(params); + params = strtok_r(NULL, "|", &p); + _pin3 = atoi(params); + + /* ********************************************************************************** + read the configuration from the EEPROM, copy it into a buffer. + ********************************************************************************** */ + getStringFromEEPROM(adrConfig, parameter); + /* ********************************************************************************** + split the config up into single parameter. As the number of parameters could be + different between multiple devices, it is done here. + This is just an example how to process the init string. Do NOT use + "," or ";" as delimiter for multiple parameters but e.g. "|" + For most customer devices it is not required. + In this case just delete the following + ********************************************************************************** */ + uint16_t Parameter1; + char *Parameter2; + params = strtok_r(parameter, "|", &p); + Parameter1 = atoi(params); + params = strtok_r(NULL, "|", &p); + Parameter2 = params; + + // In most cases you need only one of the following functions + // depending on if the constuctor takes the variables or a separate function is required + _mydevice = new (allocateMemory(sizeof(MyCustomClass))) MyCustomClass(_pin1, _pin2); + _mydevice->attach(Parameter1, Parameter2); + // if your custom device does not need a separate begin() function, delete the following + // or this function could be called from the custom constructor or attach() function + _mydevice->begin(); + } else if (_customType == 2) { + if (!FitInMemory(sizeof(MyCustomClass))) { + // Error Message to Connector + cmdMessenger.sendCmd(kStatus, F("Custom Device does not fit in Memory")); + return; + } + /* ********************************************************************************************** + read the pins from the EEPROM, copy them into a buffer + ********************************************************************************************** */ + getStringFromEEPROM(adrPin, parameter); + /* ********************************************************************************************** + split the pins up into single pins, as the number of pins could be different between + multiple devices, it is done here + ********************************************************************************************** */ + params = strtok_r(parameter, "|", &p); + _pin1 = atoi(params); + params = strtok_r(NULL, "|", &p); + _pin2 = atoi(params); + params = strtok_r(NULL, "|", &p); + _pin3 = atoi(params); + + /* ********************************************************************************** + read the configuration from the EEPROM, copy it into a buffer. + ********************************************************************************** */ + getStringFromEEPROM(adrConfig, parameter); + /* ********************************************************************************** + split the config up into single parameter. As the number of parameters could be + different between multiple devices, it is done here. + This is just an example how to process the init string. Do NOT use + "," or ";" as delimiter for multiple parameters but e.g. "|" + For most customer devices it is not required. + In this case just delete the following + ********************************************************************************** */ + uint16_t Parameter1; + char *Parameter2; + params = strtok_r(parameter, "|", &p); + Parameter1 = atoi(params); + params = strtok_r(NULL, "|", &p); + Parameter2 = params; + + // In most cases you need only one of the following functions + // depending on if the constuctor takes the variables or a separate function is required + _mydevice = new (allocateMemory(sizeof(MyCustomClass))) MyCustomClass(_pin1, _pin2); + _mydevice->attach(Parameter1, Parameter2); + // if your custom device does not need a separate begin() function, delete the following + // or this function could be called from the custom constructor or attach() function + _mydevice->begin(); + } + /* ******************************************************************************* */ +} + +/* ********************************************************************************** + The custom devives gets unregistered if a new config gets uploaded. + Keep it as it is, mostly nothing must be changed. + It gets called from CustomerDevice::Clear() +********************************************************************************** */ +void MFCustomDevice::detach() +{ + _initialized = false; + if (_customType == 1) { + _mydevice->detach(); + } else if (_customType == 2) { + _mydevice->detach(); + } +} + +/* ********************************************************************************** + Within in loop() the update() function is called regularly + Within the loop() you can define a time delay where this function gets called + or as fast as possible. See comments in loop(). + It is only needed if you have to update your custom device without getting + new values from the connector. + Otherwise just make a return; in the calling function. + It gets called from CustomerDevice::update() +********************************************************************************** */ +void MFCustomDevice::update() +{ + if (!_initialized) return; + /* ********************************************************************************** + Do something if required + ********************************************************************************** */ + if (_customType == 1) { + _mydevice->update(); + } else if (_customType == 2) { + _mydevice->update(); + } +} + +/* ********************************************************************************** + If an output for the custom device is defined in the connector, + this function gets called when a new value is available. + It gets called from CustomerDevice::OnSet() +********************************************************************************** */ +void MFCustomDevice::set(int8_t messageID, char *setPoint) +{ + if (!_initialized) return; + + if (_customType == 1) { + _mydevice->set(messageID, setPoint); + } else if (_customType == 2) { + _mydevice->set(messageID, setPoint); + } +} diff --git a/CustomDevices/_template/MFCustomDevice.h b/CustomDevices/_template/MFCustomDevice.h new file mode 100644 index 00000000..db9acf75 --- /dev/null +++ b/CustomDevices/_template/MFCustomDevice.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include "MyCustomClass.h" + +// only required if you have more than one custom device +enum { + MY_CUSTOM_DEVICE_1 = 1, + MY_CUSTOM_DEVICE_2 +}; +class MFCustomDevice +{ +public: + MFCustomDevice(uint16_t adrPin, uint16_t adrType, uint16_t adrConfig); + void detach(); + void update(); + void set(int8_t messageID, char *setPoint); + +private: + bool getStringFromEEPROM(uint16_t addreeprom, char *buffer); + bool _initialized = false; + MyCustomClass *_mydevice; + uint8_t _pin1, _pin2, _pin3; + uint8_t _customType = 0; +}; diff --git a/CustomDevices/_template/MyCustomClass.cpp b/CustomDevices/_template/MyCustomClass.cpp new file mode 100644 index 00000000..1133d9e1 --- /dev/null +++ b/CustomDevices/_template/MyCustomClass.cpp @@ -0,0 +1,71 @@ +#include "MyCustomClass.h" + +/* ********************************************************************************** + This is just the basic code to set up your custom device. + Change/add your code as needed. + If you have no class and/or only less code you could also put it + into MFCustomDevice.cpp and delete this file and MyCustomClass.h +********************************************************************************** */ + +MyCustomClass::MyCustomClass(uint8_t Pin1, uint8_t Pin2) +{ + _pin1 = Pin1; + _pin2 = Pin2; +} + +void MyCustomClass::begin() +{ +} + +void MyCustomClass::attach(uint16_t Pin3, char *init) +{ + _pin3 = Pin3; +} + +void MyCustomClass::detach() +{ + if (!_initialised) + return; + _initialised = false; +} + +void MyCustomClass::set(int8_t messageID, char *setPoint) +{ + /* ********************************************************************************** + Each messageID has it's own value + check for the messageID and define what to do. + Important Remark! + MessageID == -1 will be send from the connector when Mobiflight is closed + Put in your code to shut down your custom device (e.g. clear a display) + MessageID == -2 will be send from the connector when PowerSavingMode is entered + Put in your code to enter this mode (e.g. clear a display) + + ********************************************************************************** */ + int32_t data = atoi(setPoint); + uint16_t output; + + // do something according your messageID + switch (messageID) { + case -1: + // tbd., get's called when Mobiflight shuts down + case -2: + // tbd., get's called when PowerSavingMode is entered + case 0: + output = (uint16_t)data; + data = output; + break; + case 1: + /* code */ + break; + case 2: + /* code */ + break; + default: + break; + } +} + +void MyCustomClass::update() +{ + // Do something which is required regulary +} \ No newline at end of file diff --git a/CustomDevices/_template/MyCustomClass.h b/CustomDevices/_template/MyCustomClass.h new file mode 100644 index 00000000..c6bfa919 --- /dev/null +++ b/CustomDevices/_template/MyCustomClass.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Arduino.h" + +/* ********************************************************************************** + These defines are required to differ between multiple classes within + MFCustomDevice.cpp (see example in this file) + If you have only one class, these defines are not required as you have + not to differ. +********************************************************************************** */ +#define MY_CUSTOM_TYPE_1 "MyCustomType1" +// This define should be in the second class, it's only here to show how to handle it +#define MY_CUSTOM_TYPE_2 "MyCustomType2" +class MyCustomClass +{ +public: + MyCustomClass(uint8_t Pin1, uint8_t Pin2); + void begin(); + void attach(uint16_t Pin3, char *init); + void detach(); + void set(int8_t messageID, char *setPoint); + void update(); + +private: + bool _initialised; + uint8_t _pin1, _pin2, _pin3; +}; \ No newline at end of file diff --git a/CustomDevices/_template/MyCustomDevice_platformio.ini b/CustomDevices/_template/MyCustomDevice_platformio.ini new file mode 100644 index 00000000..1ea6459e --- /dev/null +++ b/CustomDevices/_template/MyCustomDevice_platformio.ini @@ -0,0 +1,60 @@ +; ****************************************************************************************** +; working environment for template of custom firmware +; ****************************************************************************************** +; Build settings for the Arduino Mega with Custom Firmware Template +[env:mobiflight_template_mega] +platform = atmelavr +board = megaatmega2560 +framework = arduino +; nothing needs to be cahnged above this line +build_flags = + ${env.build_flags} + -DMF_CUSTOMDEVICE_SUPPORT=1 + ;-DMF_CUSTOMDEVICE_HAS_UPDATE ; if the custom device needs to be updated, uncomment this. W/o the following define it will be done each loop() + ;-DMF_CUSTOMDEVICE_POLL_MS=10 ; time in ms between updating custom device, uncomment this if custom device needs to be updated regulary + ;-DCUSTOM_FIRMWARE_VERSION="1" ; TBD!! how to handle custom firmware versions!! + '-DMOBIFLIGHT_TYPE="Mobiflight Template Mega"' ; this must match with "MobiFlightType" within the .json file + -I./_Boards/Atmel/Board_Mega ; Include the required board definition. If you need your own definition, adapt this to your path (e.g. -I./CustomDevices/_template/_Boards) + -I./src/MF_CustomDevice ; don't change this one! + -I./CustomDevices/_template ; Include files for your custom device, replace "_template" by your folder name +build_src_filter = + ${env.build_src_filter} ; don't change this one! + +<./MF_CustomDevice> ; don't change this one! + +<../CustomDevices/_template> ; build files for your custom device, replace "_template" by your folder name +lib_deps = + ${env.lib_deps} ; don't change this one! + ${env.custom_lib_deps_Atmel} ; don't change this one! You can add additional libraries if required +monitor_speed = 115200 ; don't change this one! +extra_scripts = + ${env.extra_scripts} ; don't change this one! + +; Build settings for the Raspberry Pico with Custom Firmware Template +[env:mobiflight_template_raspberrypico] +platform = https://github.com/maxgerhardt/platform-raspberrypi.git +board = pico +framework = arduino +board_build.core = earlephilhower +board_build.filesystem_size = 0M +lib_ldf_mode = chain+ +upload_protocol = mbed +; nothing needs to be cahnged above this line +build_flags = + ${env.build_flags} + -DMF_CUSTOMDEVICE_SUPPORT=1 + ;-DMF_CUSTOMDEVICE_HAS_UPDATE ; if the custom device needs to be updated, uncomment this. W/o the following define it will be done each loop() + ;-DMF_CUSTOMDEVICE_POLL_MS=10 ; time in ms between updating custom device, uncomment this if custom device needs to be updated regulary + ;-DCUSTOM_FIRMWARE_VERSION="1" ; TBD!! how to handle custom firmware versions!! + '-DMOBIFLIGHT_TYPE="Mobiflight Template RaspiPico"' ; this must match with "MobiFlightType" within the .json file + -I./_Boards/RaspberryPi/Pico ; Include the required board definition. If you need your own definition, adapt this to your path (e.g. -I./CustomDevices/_template/_Boards) + -I./src/MF_CustomDevice ; don't change this one! + -I./CustomDevices/_template ; Include files for your custom device, replace "_template" by your folder name + -fpermissive ; don't change this one! +build_src_filter = + ${env.build_src_filter} ; don't change this one! + +<./MF_CustomDevice> ; don't change this one! + +<../CustomDevices/_template> ; build files for your custom device, replace "_template" by your folder name +lib_deps = + ${env.lib_deps} ; don't change this one!You can add additional libraries if required +monitor_speed = 115200 ; don't change this one! +extra_scripts = + ${env.extra_scripts} ; don't change this one! diff --git a/CustomDevices/_template/README.md b/CustomDevices/_template/README.md new file mode 100644 index 00000000..818f96cf --- /dev/null +++ b/CustomDevices/_template/README.md @@ -0,0 +1,6 @@ +This is a template to help you to create your own custom device. +It shows the proposed file structure and which files are required. +Within each file additional hints should guide you for setting up your code. + +The content of this file should be replaced with informations what functionality your custom device has. +Additionaly you should give the informations, if your connected hardware needs to be connected to special pins (e.g. HW SPI). diff --git a/CustomDevices/_template/connector_files/custom_mega.board.json b/CustomDevices/_template/connector_files/custom_mega.board.json new file mode 100644 index 00000000..81035c30 --- /dev/null +++ b/CustomDevices/_template/connector_files/custom_mega.board.json @@ -0,0 +1,487 @@ +{ + "$schema": "./mfboard.schema.json", + "AvrDudeSettings": { + "Device": "atmega2560", + "BaudRates": [ "115200" ], + "Programmer": "wiring", + "FirmwareBaseName": "mobiflight_template_mega", + "ResetFirmwareFile": "reset.arduino_mega_1_0_2.hex", + "Timeout": 15000 + }, + "Connection": { + "ConnectionDelay": 1250, + "ExtraConnectionRetry": false, + "ForceResetOnFirmwareUpdate": false, + "DelayAfterFirmwareUpdate": 0, + "DtrEnable": true, + "MessageSize": 90, + "EEPROMSize": 1496 + }, + "HardwareIds": [ + "^VID_2341&PID_0010", + "^VID_2341&PID_0042", + "^VID_8087&PID_0024", + "^VID_1A86&PID_7523", + "^VID_2A03&PID_0042", + "^VID_0403&PID_6001", + "^VID_0403\\+PID_6001\\+.+", + "^VID_2A03&PID_0010", + "^VID_2341&PID_0210", + "^VID_2341&PID_0242", + "^VID_10C4&PID_EA60", + "^VID_2341&PID_0044", + "^VID_3343&PID_0042", + "^VID_04D9&PID_B534" + ], + "Info": { + "CanInstallFirmware": true, + "DelayAfterFirmwareUpdate": 0, + "LatestFirmwareVersion": "0.0.1", + "FriendlyName": "Mobiflight Template Mega", + "MobiFlightType": "Mobiflight Template Mega", + "CustomDeviceType": [ + "MOBIFLIGHT_TEMPLATE", + "MOBIFLIGHT_TEMPLATE" + ] + }, + "ModuleLimits": { + "MaxAnalogInputs": 16, + "MaxInputShifters": 4, + "MaxButtons": 68, + "MaxEncoders": 20, + "MaxLcdI2C": 2, + "MaxLedSegments": 4, + "MaxOutputs": 40, + "MaxServos": 10, + "MaxShifters": 4, + "MaxSteppers": 10, + "MaxInputMultiplexer": 4, + "MaxCustomDevices": 3 + }, + "Pins": [ + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 2 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 3 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 4 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 5 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 6 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 7 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 8 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 9 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 10 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 11 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 12 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 13 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 14 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 15 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 16 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 17 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 18 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 19 + }, + { + "isAnalog": false, + "isPWM": false, + "isI2C": true, + "Pin": 20 + }, + { + "isAnalog": false, + "isPWM": false, + "isI2C": true, + "Pin": 21 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 22 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 23 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 24 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 25 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 26 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 27 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 28 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 29 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 30 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 31 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 32 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 33 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 34 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 35 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 36 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 37 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 38 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 39 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 40 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 41 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 42 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 43 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 44 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 45 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 46 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 47 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 48 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 49 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 50 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 51 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 52 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": false, + "Pin": 53 + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": false, + "Pin": 54, + "Name": "A0" + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": false, + "Pin": 55, + "Name": "A1" + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": false, + "Pin": 56, + "Name": "A2" + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": false, + "Pin": 57, + "Name": "A3" + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": false, + "Pin": 58, + "Name": "A4" + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": false, + "Pin": 59, + "Name": "A5" + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": false, + "Pin": 60, + "Name": "A6" + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": false, + "Pin": 61, + "Name": "A7" + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": false, + "Pin": 62, + "Name": "A8" + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": false, + "Pin": 63, + "Name": "A9" + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": false, + "Pin": 64, + "Name": "A10" + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": false, + "Pin": 65, + "Name": "A11" + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": false, + "Pin": 66, + "Name": "A12" + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": false, + "Pin": 67, + "Name": "A13" + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": false, + "Pin": 68, + "Name": "A14" + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": false, + "Pin": 69, + "Name": "A15" + } + ] +} diff --git a/CustomDevices/_template/connector_files/custom_raspberry_pico.board.json b/CustomDevices/_template/connector_files/custom_raspberry_pico.board.json new file mode 100644 index 00000000..4291d3b7 --- /dev/null +++ b/CustomDevices/_template/connector_files/custom_raspberry_pico.board.json @@ -0,0 +1,206 @@ +{ + "$schema": "./mfboard.schema.json", + "UsbDriveSettings": { + "VolumeLabel": "RPI-RP2", + "VerificationFileName": "INFO_UF2.TXT" + }, + "Connection": { + "ConnectionDelay": 1250, + "DelayAfterFirmwareUpdate": 1250, + "DtrEnable": true, + "EEPROMSize": 1496, + "ExtraConnectionRetry": false, + "ForceResetOnFirmwareUpdate": true, + "MessageSize": 64 + }, + "HardwareIds": ["^VID_2E8A&PID_000A"], + "Info": { + "CanInstallFirmware": true, + "CanResetBoard": true, + "FirmwareBaseName": "mobiflight_template_raspberrypico", + "FirmwareExtension": "uf2", + "FriendlyName": "MobiFlight Template RaspiPico", + "LatestFirmwareVersion": "2.4.1", + "MobiFlightType": "Mobiflight Template RaspiPico", + "ResetFirmwareFile": "reset.raspberry_pico_flash_nuke.uf2", + "CustomDeviceType": [ + "MOBIFLIGHT_TEMPLATE", + "MOBIFLIGHT_TEMPLATE2" + ] + }, + "ModuleLimits": { + "MaxAnalogInputs": 3, + "MaxInputShifters": 4, + "MaxButtons": 26, + "MaxEncoders": 8, + "MaxLcdI2C": 2, + "MaxLedSegments": 4, + "MaxOutputs": 26, + "MaxServos": 8, + "MaxShifters": 4, + "MaxSteppers": 6, + "MaxInputMultiplexer": 4, + "MaxCustomDevices": 3 + }, + "Pins": [ + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 0 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 1 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 2 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 3 + }, + { + "isAnalog": false, + "isI2C": true, + "isPWM": true, + "Pin": 4 + }, + { + "isAnalog": false, + "isI2C": true, + "isPWM": true, + "Pin": 5 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 6 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 7 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 8 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 9 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 10 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 11 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 12 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 13 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 14 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 15 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 16 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 17 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 18 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 19 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 20 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 21 + }, + { + "isAnalog": false, + "isI2C": false, + "isPWM": true, + "Pin": 22 + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": true, + "Name": "A0", + "Pin": 26 + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": true, + "Name": "A1", + "Pin": 27 + }, + { + "isAnalog": true, + "isI2C": false, + "isPWM": true, + "Name": "A2", + "Pin": 28 + } + ] + } diff --git a/CustomDevices/_template/connector_files/mobiflight.template.device.json b/CustomDevices/_template/connector_files/mobiflight.template.device.json new file mode 100644 index 00000000..662c8adb --- /dev/null +++ b/CustomDevices/_template/connector_files/mobiflight.template.device.json @@ -0,0 +1,35 @@ +{ + "$schema": "./mfdevice.schema.json", + "Info": { + "Label": "Mobiflight's template", + "Type": "MOBIFLIGHT_TEMPLATE", + "Author": "Mobiflight", + "URL": "https://github.com", + "Version" : "1.0.0" + }, + "Config": { + "Pins": [ + "Pin 1", + "Pin 2", + "Pin 3" + ], + "isI2C": false + }, + "MessageTypes": [ + { + "id": 0, + "label": "Show Speed Value", + "description": "$ will be displayed as Speed value" + }, + { + "id": 1, + "label": "Show Heading Value", + "description": "$ will be displayed as Heading value" + }, + { + "id": 2, + "label": "Show AP On/Off", + "description": "0 = True, 1 = False" + } + ] + } \ No newline at end of file diff --git a/_Boards/Atmel/Board_Mega/MFBoards.h b/_Boards/Atmel/Board_Mega/MFBoards.h index 63976fad..a5c24a41 100644 --- a/_Boards/Atmel/Board_Mega/MFBoards.h +++ b/_Boards/Atmel/Board_Mega/MFBoards.h @@ -35,6 +35,9 @@ #define MF_MUX_SUPPORT 1 #define MF_DIGIN_MUX_SUPPORT 1 #endif +#ifndef MF_CUSTOMDEVICE_SUPPORT +#define MF_CUSTOMDEVICE_SUPPORT 1 +#endif #ifndef MAX_OUTPUTS #define MAX_OUTPUTS 40 @@ -69,6 +72,9 @@ #ifndef MAX_DIGIN_MUX #define MAX_DIGIN_MUX 4 #endif +#ifndef MAX_CUSTOM_DEVICES +#define MAX_CUSTOM_DEVICES 5 +#endif #ifndef MOBIFLIGHT_TYPE #define MOBIFLIGHT_TYPE "MobiFlight Mega" diff --git a/_Boards/Atmel/Board_Nano/MFBoards.h b/_Boards/Atmel/Board_Nano/MFBoards.h index 86d0ace3..a0ccbfbf 100644 --- a/_Boards/Atmel/Board_Nano/MFBoards.h +++ b/_Boards/Atmel/Board_Nano/MFBoards.h @@ -35,6 +35,9 @@ #define MF_MUX_SUPPORT 1 #define MF_DIGIN_MUX_SUPPORT 1 #endif +#ifndef MF_CUSTOMDEVICE_SUPPORT +#define MF_CUSTOMDEVICE_SUPPORT 1 +#endif #ifndef MAX_OUTPUTS #define MAX_OUTPUTS 18 @@ -69,10 +72,9 @@ #ifndef MAX_DIGIN_MUX #define MAX_DIGIN_MUX 3 #endif - -#define STEPS 64 -#define STEPPER_SPEED 400 // 300 already worked, 467, too? -#define STEPPER_ACCEL 800 +#ifndef MAX_CUSTOM_DEVICES +#define MAX_CUSTOM_DEVICES 2 +#endif #ifndef MOBIFLIGHT_TYPE #define MOBIFLIGHT_TYPE "MobiFlight Nano" diff --git a/_Boards/Atmel/Board_ProMicro/MFBoards.h b/_Boards/Atmel/Board_ProMicro/MFBoards.h index feadacc4..64928115 100644 --- a/_Boards/Atmel/Board_ProMicro/MFBoards.h +++ b/_Boards/Atmel/Board_ProMicro/MFBoards.h @@ -35,6 +35,9 @@ #define MF_MUX_SUPPORT 1 #define MF_DIGIN_MUX_SUPPORT 1 #endif +#ifndef MF_CUSTOMDEVICE_SUPPORT +#define MF_CUSTOMDEVICE_SUPPORT 1 +#endif #ifndef MAX_OUTPUTS #define MAX_OUTPUTS 18 @@ -69,6 +72,9 @@ #ifndef MAX_DIGIN_MUX #define MAX_DIGIN_MUX 3 #endif +#ifndef MAX_CUSTOM_DEVICES +#define MAX_CUSTOM_DEVICES 2 +#endif #ifndef MOBIFLIGHT_TYPE #define MOBIFLIGHT_TYPE "MobiFlight Micro" diff --git a/_Boards/Atmel/Board_Uno/MFBoards.h b/_Boards/Atmel/Board_Uno/MFBoards.h index 4b12451a..8feded8e 100644 --- a/_Boards/Atmel/Board_Uno/MFBoards.h +++ b/_Boards/Atmel/Board_Uno/MFBoards.h @@ -35,6 +35,9 @@ #define MF_MUX_SUPPORT 1 #define MF_DIGIN_MUX_SUPPORT 1 #endif +#ifndef MF_CUSTOMDEVICE_SUPPORT +#define MF_CUSTOMDEVICE_SUPPORT 1 +#endif #ifndef MAX_OUTPUTS #define MAX_OUTPUTS 18 @@ -69,6 +72,9 @@ #ifndef MAX_DIGIN_MUX #define MAX_DIGIN_MUX 3 #endif +#ifndef MAX_CUSTOM_DEVICES +#define MAX_CUSTOM_DEVICES 2 +#endif #ifndef MOBIFLIGHT_TYPE #define MOBIFLIGHT_TYPE "MobiFlight Uno" diff --git a/_Boards/RaspberryPi/Pico/MFBoards.h b/_Boards/RaspberryPi/Pico/MFBoards.h index a56c2a8c..61dc0831 100644 --- a/_Boards/RaspberryPi/Pico/MFBoards.h +++ b/_Boards/RaspberryPi/Pico/MFBoards.h @@ -63,10 +63,9 @@ #ifndef MAX_DIGIN_MUX #define MAX_DIGIN_MUX 4 #endif - -#define STEPS 64 -#define STEPPER_SPEED 400 // 300 already worked, 467, too? -#define STEPPER_ACCEL 800 +#ifndef MAX_CUSTOM_DEVICES +#define MAX_CUSTOM_DEVICES 5 +#endif #ifndef MOBIFLIGHT_TYPE #define MOBIFLIGHT_TYPE "MobiFlight RaspiPico" diff --git a/get_version.py b/get_version.py index 3b8cc853..a1f94183 100644 --- a/get_version.py +++ b/get_version.py @@ -23,4 +23,4 @@ ]) # Set the output filename to the name of the board and the version -env.Replace(PROGNAME=f'mobiflight_{env["PIOENV"]}_{firmware_version.replace(".", "_")}') +env.Replace(PROGNAME=f'{env["PIOENV"]}_{firmware_version.replace(".", "_")}') diff --git a/platformio.ini b/platformio.ini index 92f017c8..dc39a3b5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,6 +15,9 @@ ; development use VSCode to change the target to a non-default ; by clicking on the target name in the bottom status bar. [platformio] +; include custom environments to be build +extra_configs = + ./CustomDevices/_template/MyCustomDevice_platformio.ini ; Common build settings across all devices [env] @@ -48,20 +51,22 @@ build_flags = -I./src/MF_Modules build_src_filter = +<*> + -<./MF_CustomDevice> extra_scripts = pre:get_version.py ; Build settings for the Arduino Mega -[env:mega] +[env:mobiflight_mega] platform = atmelavr board = megaatmega2560 framework = arduino build_flags = ${env.build_flags} + -DMF_CUSTOMDEVICE_SUPPORT=0 + '-DMOBIFLIGHT_TYPE="MobiFlight Mega"' -I./_Boards/Atmel/Board_Mega build_src_filter = ${env.build_src_filter} - +<../_Boards/Atmel> lib_deps = ${env.lib_deps} ${env.custom_lib_deps_Atmel} @@ -70,16 +75,17 @@ extra_scripts = ${env.extra_scripts} ; Build settings for the Arduino Pro Micro -[env:micro] +[env:mobiflight_micro] platform = atmelavr board = sparkfun_promicro16 framework = arduino build_flags = ${env.build_flags} + -DMF_CUSTOMDEVICE_SUPPORT=0 + '-DMOBIFLIGHT_TYPE="MobiFlight Micro"' -I./_Boards/Atmel/Board_ProMicro build_src_filter = ${env.build_src_filter} - +<../_Boards/Atmel> lib_deps = ${env.lib_deps} ${env.custom_lib_deps_Atmel} @@ -89,16 +95,17 @@ extra_scripts = ; Build settings for the Arduino Uno -[env:uno] +[env:mobiflight_uno] platform = atmelavr board = uno framework = arduino build_flags = ${env.build_flags} + -DMF_CUSTOMDEVICE_SUPPORT=0 + '-DMOBIFLIGHT_TYPE="MobiFlight Uno"' -I./_Boards/Atmel/Board_Uno build_src_filter = ${env.build_src_filter} - +<../_Boards/Atmel> lib_deps = ${env.lib_deps} ${env.custom_lib_deps_Atmel} @@ -107,16 +114,17 @@ extra_scripts = ${env.extra_scripts} ; Build settings for the Arduino Nano -[env:nano] +[env:mobiflight_nano] platform = atmelavr board = nanoatmega328 framework = arduino build_flags = ${env.build_flags} + -DMF_CUSTOMDEVICE_SUPPORT=0 + '-DMOBIFLIGHT_TYPE="MobiFlight Nano"' -I./_Boards/Atmel/Board_Nano build_src_filter = ${env.build_src_filter} - +<../_Boards/Atmel> lib_deps = ${env.lib_deps} ${env.custom_lib_deps_Atmel} @@ -125,25 +133,26 @@ extra_scripts = ${env.extra_scripts} ; Build settings for the Raspberry Pico original -[env:raspberrypico] +[env:mobiflight_raspberrypico] platform = https://github.com/maxgerhardt/platform-raspberrypi.git board = pico framework = arduino -board_build.core = earlephilhower ; select new core -board_build.filesystem_size = 0M ; configure filesystem size. Default 0 Mbyte. +board_build.core = earlephilhower ; select new core +board_build.filesystem_size = 0M ; configure filesystem size. Default 0 Mbyte. lib_ldf_mode = chain+ -upload_protocol = mbed ; for debugging upoading can be changed to picoprobe -;debug_tool = picoprobe ; and uncomment this for debugging w/ picoprobe +upload_protocol = mbed ; for debugging upoading can be changed to picoprobe +;debug_tool = picoprobe ; and uncomment this for debugging w/ picoprobe build_flags = ${env.build_flags} - -DUSE_INTERRUPT + -DMF_CUSTOMDEVICE_SUPPORT=0 + '-DMOBIFLIGHT_TYPE="MobiFlight RaspiPico"' ; this must match with "MobiFlightType" within the .json file -I./_Boards/RaspberryPi/Pico -fpermissive build_src_filter = ${env.build_src_filter} - +<../_Boards/RaspberryPi> lib_deps = ${env.lib_deps} monitor_speed = 115200 extra_scripts = - ${env.extra_scripts} \ No newline at end of file + ${env.extra_scripts} + diff --git a/src/CommandMessenger.cpp b/src/CommandMessenger.cpp index a82c526d..5870ae34 100644 --- a/src/CommandMessenger.cpp +++ b/src/CommandMessenger.cpp @@ -33,6 +33,9 @@ #if MF_DIGIN_MUX_SUPPORT == 1 #include "DigInMux.h" #endif +#if MF_CUSTOMDEVICE_SUPPORT == 1 +#include "CustomDevice.h" +#endif CmdMessenger cmdMessenger = CmdMessenger(Serial); unsigned long lastCommand; @@ -83,6 +86,10 @@ void attachCommandCallbacks() cmdMessenger.attach(kSetShiftRegisterPins, OutputShifter::OnSet); #endif +#if MF_CUSTOMDEVICE_SUPPORT == 1 + cmdMessenger.attach(kSetCustomDevice, CustomDevice::OnSet); +#endif + #ifdef DEBUG2CMDMESSENGER cmdMessenger.sendCmd(kDebug, F("Attached callbacks")); #endif diff --git a/src/Config.cpp b/src/Config.cpp index 0445483e..171daa3a 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -38,11 +38,15 @@ #if MF_DIGIN_MUX_SUPPORT == 1 #include "DigInMux.h" #endif +#if MF_CUSTOMDEVICE_SUPPORT == 1 +#include "CustomDevice.h" +#endif // The build version comes from an environment variable #define STRINGIZER(arg) #arg #define STR_VALUE(arg) STRINGIZER(arg) -#define VERSION STR_VALUE(BUILD_VERSION) +#define VERSION STR_VALUE(BUILD_VERSION) + MFEEPROM MFeeprom; #if MF_MUX_SUPPORT == 1 @@ -78,8 +82,7 @@ bool readConfigLength() while (MFeeprom.read_byte(addreeprom++) != 0x00) { configLength++; - if (addreeprom > length) - { + if (addreeprom > length) { cmdMessenger.sendCmd(kStatus, F("Loading config failed")); // text or "-1" like config upload? return false; } @@ -147,6 +150,10 @@ void resetConfig() #if MF_DIGIN_MUX_SUPPORT == 1 DigInMux::Clear(); #endif +#if MF_CUSTOMDEVICE_SUPPORT == 1 + CustomDevice::Clear(); +#endif + configLength = 0; configActivated = false; ClearMemory(); @@ -214,8 +221,10 @@ bool readNameFromEEPROM(uint16_t *addreeprom, char *buffer, uint16_t *addrbuffer return true; } -// reads the EEPRROM until end of command which ':' terminated -bool readEndCommandFromEEPROM(uint16_t *addreeprom) +// steps thru the EEPRROM until the delimiter is detected +// it could be ":" for end of one device config +// or "." for end of type/pin/config entry for custom device +bool readEndCommandFromEEPROM(uint16_t *addreeprom, uint8_t delimiter) { char temp = 0; uint16_t length = MFeeprom.get_length(); @@ -223,7 +232,7 @@ bool readEndCommandFromEEPROM(uint16_t *addreeprom) temp = MFeeprom.read_byte((*addreeprom)++); if (*addreeprom > length) // abort if EEPROM size will be exceeded return false; - } while (temp != ':'); // reads until limiter ':' + } while (temp != delimiter); // reads until delimiter return true; } @@ -237,8 +246,13 @@ void readConfig() char command = readUintFromEEPROM(&addreeprom); // read the first value from EEPROM, it's a device definition bool copy_success = true; // will be set to false if copying input names to nameBuffer exceeds array dimensions // not required anymore when pins instead of names are transferred to the UI +#if MF_CUSTOMDEVICE_SUPPORT == 1 + uint16_t adrPin = 0; // at this position in the EEPROM the custom device pins are stored + uint16_t adrType = 0; // at this position in the EEPROM the custom device type is stored + uint16_t adrConfig = 0; // at this position in the EEPROM the custom device config is stored +#endif - if (command == 0) // just to be sure, configLength should also be 0 + if (command == 0) // just to be sure, configLength should also be 0 return; do // go through the EEPROM until it is NULL terminated @@ -253,7 +267,7 @@ void readConfig() case kTypeOutput: params[0] = readUintFromEEPROM(&addreeprom); // Pin number Output::Add(params[0]); - copy_success = readEndCommandFromEEPROM(&addreeprom); // check EEPROM until end of name + copy_success = readEndCommandFromEEPROM(&addreeprom, ':'); // check EEPROM until end of name break; #if MF_SEGMENT_SUPPORT == 1 @@ -264,7 +278,7 @@ void readConfig() params[3] = readUintFromEEPROM(&addreeprom); // brightness params[4] = readUintFromEEPROM(&addreeprom); // number of modules LedSegment::Add(params[0], params[1], params[2], params[4], params[3]); - copy_success = readEndCommandFromEEPROM(&addreeprom); // check EEPROM until end of name + copy_success = readEndCommandFromEEPROM(&addreeprom, ':'); // check EEPROM until end of name break; #endif @@ -277,7 +291,7 @@ void readConfig() params[3] = readUintFromEEPROM(&addreeprom); // Pin4 number params[4] = readUintFromEEPROM(&addreeprom); // Button number Stepper::Add(params[0], params[1], params[2], params[3], 0); - copy_success = readEndCommandFromEEPROM(&addreeprom); // check EEPROM until end of name + copy_success = readEndCommandFromEEPROM(&addreeprom, ':'); // check EEPROM until end of name break; case kTypeStepperDeprecated2: @@ -287,7 +301,7 @@ void readConfig() params[3] = readUintFromEEPROM(&addreeprom); // Pin4 number params[4] = readUintFromEEPROM(&addreeprom); // Button number Stepper::Add(params[0], params[1], params[2], params[3], params[4]); - copy_success = readEndCommandFromEEPROM(&addreeprom); // check EEPROM until end of name + copy_success = readEndCommandFromEEPROM(&addreeprom, ':'); // check EEPROM until end of name break; case kTypeStepper: @@ -302,7 +316,7 @@ void readConfig() // there is an additional 9th parameter stored in the config (profileID) which is not needed in the firmware // and therefor not read in, it is just skipped like the name with reading until end of command Stepper::Add(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7]); - copy_success = readEndCommandFromEEPROM(&addreeprom); // check EEPROM until end of name + copy_success = readEndCommandFromEEPROM(&addreeprom, ':'); // check EEPROM until end of name break; #endif @@ -310,7 +324,7 @@ void readConfig() case kTypeServo: params[0] = readUintFromEEPROM(&addreeprom); // Pin number Servos::Add(params[0]); - copy_success = readEndCommandFromEEPROM(&addreeprom); // check EEPROM until end of name + copy_success = readEndCommandFromEEPROM(&addreeprom, ':'); // check EEPROM until end of name break; #endif @@ -319,7 +333,7 @@ void readConfig() params[1] = readUintFromEEPROM(&addreeprom); // Pin2 number Encoder::Add(params[0], params[1], 0, &nameBuffer[addrbuffer]); // MUST be before readNameFromEEPROM because readNameFromEEPROM returns the pointer for the NEXT Name copy_success = readNameFromEEPROM(&addreeprom, nameBuffer, &addrbuffer); // copy the NULL terminated name to nameBuffer and set to next free memory location - // copy_success = readEndCommandFromEEPROM(&addreeprom); // once the nameBuffer is not required anymore uncomment this line and delete the line before + // copy_success = readEndCommandFromEEPROM(&addreeprom, ':'); // once the nameBuffer is not required anymore uncomment this line and delete the line before break; case kTypeEncoder: @@ -328,7 +342,7 @@ void readConfig() params[2] = readUintFromEEPROM(&addreeprom); // type Encoder::Add(params[0], params[1], params[2], &nameBuffer[addrbuffer]); // MUST be before readNameFromEEPROM because readNameFromEEPROM returns the pointer for the NEXT Name copy_success = readNameFromEEPROM(&addreeprom, nameBuffer, &addrbuffer); // copy the NULL terminated name to to nameBuffer and set to next free memory location - // copy_success = readEndCommandFromEEPROM(&addreeprom); // once the nameBuffer is not required anymore uncomment this line and delete the line before + // copy_success = readEndCommandFromEEPROM(&addreeprom, ':'); // once the nameBuffer is not required anymore uncomment this line and delete the line before break; #if MF_LCD_SUPPORT == 1 @@ -337,7 +351,7 @@ void readConfig() params[1] = readUintFromEEPROM(&addreeprom); // columns params[2] = readUintFromEEPROM(&addreeprom); // lines LCDDisplay::Add(params[0], params[1], params[2]); - copy_success = readEndCommandFromEEPROM(&addreeprom); // check EEPROM until end of name + copy_success = readEndCommandFromEEPROM(&addreeprom, ':'); // check EEPROM until end of name break; #endif @@ -347,7 +361,7 @@ void readConfig() params[1] = readUintFromEEPROM(&addreeprom); // sensitivity Analog::Add(params[0], &nameBuffer[addrbuffer], params[1]); // MUST be before readNameFromEEPROM because readNameFromEEPROM returns the pointer for the NEXT Name copy_success = readNameFromEEPROM(&addreeprom, nameBuffer, &addrbuffer); // copy the NULL terminated name to to nameBuffer and set to next free memory location - // copy_success = readEndCommandFromEEPROM(&addreeprom); // once the nameBuffer is not required anymore uncomment this line and delete the line before + // copy_success = readEndCommandFromEEPROM(&addreeprom, ':'); // once the nameBuffer is not required anymore uncomment this line and delete the line before break; #endif @@ -358,7 +372,7 @@ void readConfig() params[2] = readUintFromEEPROM(&addreeprom); // data Pin params[3] = readUintFromEEPROM(&addreeprom); // number of daisy chained modules OutputShifter::Add(params[0], params[1], params[2], params[3]); - copy_success = readEndCommandFromEEPROM(&addreeprom); // check EEPROM until end of name + copy_success = readEndCommandFromEEPROM(&addreeprom, ':'); // check EEPROM until end of name break; #endif @@ -370,7 +384,7 @@ void readConfig() params[3] = readUintFromEEPROM(&addreeprom); // number of daisy chained modules InputShifter::Add(params[0], params[1], params[2], params[3], &nameBuffer[addrbuffer]); copy_success = readNameFromEEPROM(&addreeprom, nameBuffer, &addrbuffer); // copy the NULL terminated name to to nameBuffer and set to next free memory location - // copy_success = readEndCommandFromEEPROM(&addreeprom); // once the nameBuffer is not required anymore uncomment this line and delete the line before + // copy_success = readEndCommandFromEEPROM(&addreeprom, ':'); // once the nameBuffer is not required anymore uncomment this line and delete the line before break; #endif @@ -404,8 +418,30 @@ void readConfig() break; #endif +#if MF_CUSTOMDEVICE_SUPPORT == 1 + case kTypeCustomDevice: + adrType = addreeprom; // first location of custom Type in EEPROM + copy_success = readEndCommandFromEEPROM(&addreeprom, '.'); + if (!copy_success) + break; + + adrPin = addreeprom; // first location of custom pins in EEPROM + copy_success = readEndCommandFromEEPROM(&addreeprom, '.'); + if (!copy_success) + break; + + adrConfig = addreeprom; // first location of custom config in EEPROM + copy_success = readEndCommandFromEEPROM(&addreeprom, '.'); + if (copy_success) { + CustomDevice::Add(adrPin, adrType, adrConfig); + copy_success = readEndCommandFromEEPROM(&addreeprom, ':'); // check EEPROM until end of command + } + // cmdMessenger.sendCmd(kDebug, F("CustomDevice loaded")); + break; +#endif + default: - copy_success = readEndCommandFromEEPROM(&addreeprom); // check EEPROM until end of name + copy_success = readEndCommandFromEEPROM(&addreeprom, ':'); // check EEPROM until end of name } command = readUintFromEEPROM(&addreeprom); } while (command && copy_success); diff --git a/src/MF_CustomDevice/CustomDevice.cpp b/src/MF_CustomDevice/CustomDevice.cpp new file mode 100644 index 00000000..2e853a32 --- /dev/null +++ b/src/MF_CustomDevice/CustomDevice.cpp @@ -0,0 +1,85 @@ +#include +#include "CustomDevice.h" +#include "MFCustomDevice.h" +#include "commandmessenger.h" +#include "MFBoards.h" +#include "allocateMem.h" + +/* ********************************************************************************** + Normally nothing has to be changed in this file + It handles one or multiple custom devices +********************************************************************************** */ +namespace CustomDevice +{ + uint8_t CustomDeviceRegistered = 0; + MFCustomDevice *customDevice[MAX_CUSTOM_DEVICES]; + + void Add(uint16_t adrPin, uint16_t adrType, uint16_t adrConfig) + { + if (CustomDeviceRegistered == MAX_CUSTOM_DEVICES) + return; + if (!FitInMemory(sizeof(MFCustomDevice))) { + // Error Message to Connector + cmdMessenger.sendCmd(kStatus, F("Custom Device does not fit in Memory")); + return; + } + customDevice[CustomDeviceRegistered] = new (allocateMemory(sizeof(MFCustomDevice))) MFCustomDevice(adrPin, adrType, adrConfig); + CustomDeviceRegistered++; +#ifdef DEBUG2CMDMESSENGER + cmdMessenger.sendCmd(kStatus, F("Added Stepper Setpoint")); +#endif + } + + /* ********************************************************************************** + All custom devives gets unregistered if a new config gets uploaded. + The Clear() function from every registerd custom device will be called. + Keep it as it is, mostly nothing must be changed + ********************************************************************************** */ + void Clear() + { + for (int i = 0; i != CustomDeviceRegistered; i++) { + customDevice[i]->detach(); + } + CustomDeviceRegistered = 0; +#ifdef DEBUG2CMDMESSENGER + cmdMessenger.sendCmd(kStatus, F("Cleared Stepper Setpoint")); +#endif + } + + /* ********************************************************************************** + Within in loop() the update() function is called regularly + Within the loop() you can define a time delay where this function gets called + or as fast as possible. See comments in loop() + The update() function from every registerd custom device will be called. + It is only needed if you have to update your custom device without getting + new values from the connector. Otherwise just make a return; + ********************************************************************************** */ + void update() + { + for (int i = 0; i != CustomDeviceRegistered; i++) { + customDevice[i]->update(); + } + } + + /* ********************************************************************************** + If an output for the custom device is defined in the connector, + this function gets called when a new value is available. + In this case the connector sends a messageID followed by a string which is not longer + than 90 Byte (!!check the length of the string!!) limited by the commandMessenger. + The messageID is used to mark the value which has changed. This reduced the serial + communication as not all values has to be send in one (big) string (like for the LCD) + The OnSet() function from every registerd custom device will be called. + ********************************************************************************** */ + void OnSet() + { + int16_t device = cmdMessenger.readInt16Arg(); // get the device number + if (device >= CustomDeviceRegistered) // and do nothing if this device is not registered + return; + int16_t messageID = cmdMessenger.readInt16Arg(); // get the messageID number + char *output = cmdMessenger.readStringArg(); // get the pointer to the new raw string + cmdMessenger.unescape(output); // and unescape the string if escape characters are used + customDevice[device]->set(messageID, output); // send the string to your custom device + + setLastCommandMillis(); + } +} // end of namespace diff --git a/src/MF_CustomDevice/CustomDevice.h b/src/MF_CustomDevice/CustomDevice.h new file mode 100644 index 00000000..e4118b84 --- /dev/null +++ b/src/MF_CustomDevice/CustomDevice.h @@ -0,0 +1,9 @@ +#pragma once + +namespace CustomDevice +{ + void Add(uint16_t adrPin, uint16_t adrType, uint16_t adrConfig); + void Clear(); + void update(); + void OnSet(); +} diff --git a/src/commandmessenger.h b/src/commandmessenger.h index 4e3382f7..eddfd1a3 100644 --- a/src/commandmessenger.h +++ b/src/commandmessenger.h @@ -44,6 +44,7 @@ enum { kInputShifterChange, // 29 kDigInMuxChange, // 30 kSetStepperSpeedAccel, // 31 + kSetCustomDevice, // 32 kDebug = 0xFF // 255 }; diff --git a/src/config.h b/src/config.h index a882abad..84a0dd17 100644 --- a/src/config.h +++ b/src/config.h @@ -22,7 +22,8 @@ enum { kTypeInputShifter, // 12 Input shift register support (example: 74HC165) kTypeMuxDriver, // 13 Multiplexer selector support (generates select outputs) kTypeDigInMux, // 14 Digital input multiplexer support (example: 74HCT4067, 74HCT4051) - kTypeStepper // new stepper type with settings for backlash and deactivate output + kTypeStepper, // new stepper type with settings for backlash and deactivate output + kTypeCustomDevice // 15 Custom Device }; void loadConfig(void); diff --git a/src/mobiflight.cpp b/src/mobiflight.cpp index 737df09a..f462a5fa 100644 --- a/src/mobiflight.cpp +++ b/src/mobiflight.cpp @@ -32,6 +32,9 @@ #if MF_DIGIN_MUX_SUPPORT == 1 #include "DigInMux.h" #endif +#if MF_CUSTOMDEVICE_SUPPORT == 1 +#include "CustomDevice.h" +#endif #define MF_BUTTON_DEBOUNCE_MS 10 // time between updating the buttons #define MF_ENCODER_DEBOUNCE_MS 1 // time between encoder updates @@ -67,6 +70,9 @@ typedef struct { #if MF_DIGIN_MUX_SUPPORT == 1 uint32_t DigInMux = 0; #endif +#if MF_CUSTOMDEVICE_SUPPORT == 1 + uint32_t CustomDevice = 0; +#endif } lastUpdate_t; lastUpdate_t lastUpdate; @@ -196,6 +202,15 @@ void loop() #if MF_DIGIN_MUX_SUPPORT == 1 timedUpdate(DigInMux::read, &lastUpdate.DigInMux, MF_INMUX_POLL_MS); #endif + +#if MF_CUSTOMDEVICE_SUPPORT == 1 && defined(MF_CUSTOMDEVICE_HAS_UPDATE) +#ifdef MF_CUSTOMDEVICE_POLL_MS + timedUpdate(CustomDevice::update, &lastUpdate.CustomDevice, MF_CUSTOMDEVICE_POLL_MS); +#else + CustomDevice::update(); +#endif +#endif + // lcds, outputs, outputshifters, segments do not need update } }