diff --git a/docs/moonbase/inputoutput.md b/docs/moonbase/inputoutput.md
index 506b2cd06..d870af680 100644
--- a/docs/moonbase/inputoutput.md
+++ b/docs/moonbase/inputoutput.md
@@ -33,9 +33,10 @@ For each board the following presets are defined:
* Button LightsOn 🛎️/𓐟: sets on/off in [Light Control](../../moonlight/lightscontrol/), Push (🛎️) and Toggle (𓐟)
* Relay LightsOn 🔀: sets on/off in [Light Control](../../moonlight/lightscontrol/)
* SPI_SCK, SPI_MISO, SPI_MOSI, PHY_CS, PHY_IRQ 🔗: S3 Ethernet, Used by the Ethernet module, see [Ethernet](../../network/ethernet/)
+ * High / Low: to indefinitely set high or low a GPIO pin
+ * RS-485 DE/TX/RX: when all set, for upcoming DMX and RS485 communications
* Planned soon
* Battery
- * DMX
* Planned later
* I2S for microphone and line in
* I2C
@@ -86,7 +87,16 @@ For each board the following presets are defined:
### SE16 v1
-{: style="width:100px"}
+
* Choose the esp32-s3-devkitc-1-n8r8v board in the [MoonLight Installer](../../gettingstarted/installer/)
-* Set Switch1 the same as you set the jumper on the board: off / default: Infrared. on: Ethernet.
\ No newline at end of file
+* Set Switch1 the same as you set the jumper on the board: off / default: Infrared. on: Ethernet.
+* Only 5 boards were ever produced. If you are one of the lucky few, feel free to reach out to limpkin on [Discord](https://discord.gg/TC8NSUSCdV)
+
+
+### LightCrafter16
+
+
+
+* Choose the esp32-s3-devkitc-1-n8r8v board in the [MoonLight Installer](../../gettingstarted/installer/)
+* Documentation to be soon published on [limpkin's website](https://www.limpkin.fr)
\ No newline at end of file
diff --git a/docs/moonlight/layouts.md b/docs/moonlight/layouts.md
index d66c8f860..17d746dd8 100644
--- a/docs/moonlight/layouts.md
+++ b/docs/moonlight/layouts.md
@@ -26,6 +26,7 @@ Want to add a a Layout to MoonLight, see [develop](https://moonmodules.org/MoonL
| Single Line |  |
| |
| Single Row |  |
| |
| SE16 |  |
| Layout(s) including pins for Stephan Electronics 16-Pin ESP32-S3 board
see below |
+| LightCrafter16 |  |
| Layout(s) for Stephan Electronics LightCrafter16 ESP32-S3 board
see below |
!!! warning "Choosing pins"
@@ -36,11 +37,21 @@ Want to add a a Layout to MoonLight, see [develop](https://moonmodules.org/MoonL
### SE16
-16 channel LED strip driver by Stephane Electronics
+16-channel LED strip driver by Stephan Electronics
-
+
* Leds Per Pin: the number of LEDs connected to each pin
* Pins Are Columns: are the LEDs on a pin a row of the effect (width is 1 (or 2) x ledsPerPin). If not set the LEDs are a column (height is 1 (or 2) x ledsPerPin)
* Mirrored Pins: If set it is assumed that LEDs are connected with increasing positions on 8 pins on one side of the board and decreasing positions on the 8 pins of the other side of the board. The resulting size will have a width of 8 and the height (or width) will be 2 * ledsPerPin. If not set, the width will be 16 and the height (or width) = ledsPerPin
-* Pins: 47,48,21,38,14,39,13,40,12,41,11,42,10,2,3,1
+
+### LightCrafter16
+
+16-channel LED strip driver by Stephan Electronics
+
+
+
+* Leds Per Pin: the number of LEDs connected to each pin
+* Pins Are Columns: are the LEDs on a pin a row of the effect (width is 1 (or 2) x ledsPerPin). If not set the LEDs are a column (height is 1 (or 2) x ledsPerPin)
+
+X0Y0 position is on the top left when the board is positioned in such a way that the Ethernet connector is on the top left.
\ No newline at end of file
diff --git a/firmware/installer/images/esp32-s3-lightcrafter16.jpg b/firmware/installer/images/esp32-s3-lightcrafter16.jpg
new file mode 100644
index 000000000..db5528b1a
Binary files /dev/null and b/firmware/installer/images/esp32-s3-lightcrafter16.jpg differ
diff --git a/firmware/installer/images/esp32-s3-stephanelec-16p.jpg b/firmware/installer/images/esp32-s3-stephanelec-16p.jpg
index b0e7c1e7c..c9412a128 100644
Binary files a/firmware/installer/images/esp32-s3-stephanelec-16p.jpg and b/firmware/installer/images/esp32-s3-stephanelec-16p.jpg differ
diff --git a/src/MoonBase/Modules/ModuleIO.h b/src/MoonBase/Modules/ModuleIO.h
index 9699218a0..38942c3a4 100644
--- a/src/MoonBase/Modules/ModuleIO.h
+++ b/src/MoonBase/Modules/ModuleIO.h
@@ -15,6 +15,7 @@
#if FT_MOONBASE == 1
#include "MoonBase/Module.h"
+ #include "driver/uart.h"
enum IO_PinUsageEnum {
pin_Unused, // 0
@@ -80,7 +81,7 @@ enum IO_BoardsEnum {
board_SergUniShieldV5,
board_SergMiniShield,
board_SE16V1,
- board_SE16V2,
+ board_LightCrafter16,
board_MHCV43, // by Wladi
board_MHCP4NanoV1, // by Wladi V1.0
board_YvesV48,
@@ -309,15 +310,15 @@ class ModuleIO : public Module {
pinAssigner.assignPin(5, pin_Infrared);
}
- } else if (boardID == board_SE16V2) {
+ } else if (boardID == board_LightCrafter16) {
object["maxPower"] = 500;
uint8_t ledPins[] = {47, 21, 14, 9, 8, 16, 15, 7, 1, 2, 42, 41, 40, 39, 38, 48}; // LED_PINS
for (uint8_t gpio : ledPins) pinAssigner.assignPin(gpio, pin_LED);
pinAssigner.assignPin(3, pin_High); // WIZ850_nRST, needs to be high to access RS485_DE, VBUS_DET, WIZ580_nINT. Also drives an LED.
gpio_set_direction((gpio_num_t)3, GPIO_MODE_OUTPUT); // LEAVE here: guarantees the pin is set to high at platform boot so the ethernet module can initialize correctly
gpio_set_level((gpio_num_t)3, 1); // LEAVE here: guarantees the pin is set to high at platform boot so the ethernet module can initialize correctly
- pinAssigner.assignPin(17, pin_Serial_TX);
- pinAssigner.assignPin(18, pin_Serial_RX);
+ pinAssigner.assignPin(17, pin_RS485_TX);
+ pinAssigner.assignPin(18, pin_RS485_RX);
pinAssigner.assignPin(46, pin_RS485_DE);
pinAssigner.assignPin(0, pin_Dig_Input); // Native USB port vbus detection
pinAssigner.assignPin(5, pin_Voltage); // Input voltage
@@ -602,7 +603,12 @@ class ModuleIO : public Module {
}
}
+
void readPins() {
+ uint8_t pinRS485TX = UINT8_MAX;
+ uint8_t pinRS485RX = UINT8_MAX;
+ uint8_t pinRS485DE = UINT8_MAX;
+
#if FT_ENABLED(FT_ETHERNET)
EXT_LOGD(MB_TAG, "Try to configure ethernet");
EthernetSettingsService* ess = _sveltekit->getEthernetSettingsService();
@@ -664,7 +670,15 @@ class ModuleIO : public Module {
} else if (usage == pin_Battery) {
pinBattery = pinObject["GPIO"];
EXT_LOGD(ML_TAG, "pinBattery found %d", pinBattery);
- } else if (usage == pin_High) {
+ }
+ }
+ #endif
+
+ // GPIOs & RS485
+ bool rs485_ios_updated = false;
+ for (JsonObject pinObject : _state.data["pins"].as()) {
+ uint8_t usage = pinObject["usage"];
+ if (usage == pin_High) {
uint8_t pinHigh = pinObject["GPIO"];
if (GPIO_IS_VALID_OUTPUT_GPIO(pinHigh)) {
gpio_set_direction((gpio_num_t)pinHigh, GPIO_MODE_OUTPUT);
@@ -678,9 +692,62 @@ class ModuleIO : public Module {
gpio_set_level((gpio_num_t)pinLow, 0);
}
EXT_LOGD(ML_TAG, "Setting pin %d to low", pinLow);
- }
+ } else if (usage == pin_RS485_DE) {
+ rs485_ios_updated = true;
+ pinRS485DE = pinObject["GPIO"];
+ } else if (usage == pin_RS485_RX) {
+ rs485_ios_updated = true;
+ pinRS485RX = pinObject["GPIO"];
+ } else if (usage == pin_RS485_TX) {
+ rs485_ios_updated = true;
+ pinRS485TX = pinObject["GPIO"];
+ }
+ }
+
+ // Check if all RS485 pins are specified
+ if (rs485_ios_updated && (pinRS485TX != UINT8_MAX) && (pinRS485RX != UINT8_MAX) && (pinRS485DE != UINT8_MAX)) {
+ EXT_LOGD(ML_TAG, "RS485 init with pins %d %d %d", pinRS485TX, pinRS485RX, pinRS485DE);
+
+ // test code to be replaced with functional code. use UART1 as UART0 is (AFAIK) always for debug serial
+ uart_config_t uart_config = {
+ .baud_rate = 9600,
+ .data_bits = UART_DATA_8_BITS,
+ .parity = UART_PARITY_DISABLE,
+ .stop_bits = UART_STOP_BITS_1,
+ .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, // Flow control handled by RS485 driver
+ .source_clk = UART_SCLK_DEFAULT,
+ };
+ uart_driver_delete(UART_NUM_1);
+ ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, 128 * 2, 0, 0, NULL, 0));
+ ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &uart_config));
+ ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, pinRS485TX, pinRS485RX, pinRS485DE, UART_PIN_NO_CHANGE));
+ ESP_ERROR_CHECK(uart_set_mode(UART_NUM_1, UART_MODE_RS485_HALF_DUPLEX));
+
+ #ifdef DEMOCODE_FOR_SHT30_SENSOR
+ // Modbus RTU Request: [Addr][Func][RegHi][RegLo][CountHi][CountLo][CRC_L][CRC_H]
+ // To read Reg 0 & 1 from Slave 0x01: 01 03 00 00 00 02 C4 0B
+ const uint8_t request[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0xC4, 0x0B};
+ uint8_t response[128];
+ // Send request
+ uart_write_bytes(UART_NUM_1, (const char*)request, sizeof(request));
+ EXT_LOGD(ML_TAG, "Request sent");
+
+ // Wait for response (timeout 1 second)
+ int len = uart_read_bytes(UART_NUM_1, response, 128, pdMS_TO_TICKS(100));
+
+ if (len > 8) {
+ EXT_LOGD(ML_TAG, "Answer received: %d %d %d %d %d %d %d %d %d", response[0], response[1], response[2], response[3], response[4], response[5], response[6], response[7], response[8]);
+ float humidity = ((float)response[3])*256 + (float)response[4];
+ float temperature = ((float)response[5])*256 +(float)response[6];
+ EXT_LOGD(ML_TAG, "humidity: %f temperature: %f", humidity/10, temperature/10);
+ // Process registers here (response[3] to response[6] contain the data)
+ } else if (len > 0) {
+ EXT_LOGD(ML_TAG, "Invalid answer length");
+ } else {
+ EXT_LOGD(ML_TAG, "No response from sensor");
+ }
+ #endif
}
- #endif
}
#if FT_BATTERY
diff --git a/src/MoonLight/Modules/ModuleDrivers.h b/src/MoonLight/Modules/ModuleDrivers.h
index 84de21397..51926a59d 100644
--- a/src/MoonLight/Modules/ModuleDrivers.h
+++ b/src/MoonLight/Modules/ModuleDrivers.h
@@ -99,8 +99,12 @@ class ModuleDrivers : public NodeManager {
addControlValue(control, getNameAndTags());
addControlValue(control, getNameAndTags());
- // custom
- addControlValue(control, getNameAndTags());
+ // board preset specific
+ _moduleIO->read([&](ModuleState& state) {
+ uint8_t boardPreset = state.data["boardPreset"];
+ if (boardPreset == board_SE16V1) addControlValue(control, getNameAndTags());
+ if (boardPreset == board_LightCrafter16) addControlValue(control, getNameAndTags());
+ });
}
Node* addNode(const uint8_t index, const char* name, const JsonArray& controls) const override {
@@ -128,8 +132,12 @@ class ModuleDrivers : public NodeManager {
if (!node) node = checkAndAlloc(name);
if (!node) node = checkAndAlloc(name);
- // custom
- if (!node) node = checkAndAlloc(name);
+ // board preset specific
+ _moduleIO->read([&](ModuleState& state) {
+ uint8_t boardPreset = state.data["boardPreset"];
+ if (!node && boardPreset == board_SE16V1) node = checkAndAlloc(name);
+ if (!node && boardPreset == board_LightCrafter16) node = checkAndAlloc(name);
+ });
#if FT_LIVESCRIPT
if (!node) {
diff --git a/src/MoonLight/Nodes/Layouts/L_SE16.h b/src/MoonLight/Nodes/Layouts/L_SE16.h
index e007759dd..1f98c981e 100644
--- a/src/MoonLight/Nodes/Layouts/L_SE16.h
+++ b/src/MoonLight/Nodes/Layouts/L_SE16.h
@@ -80,4 +80,52 @@ class SE16Layout : public Node {
}
};
+// LightCrafter16 board
+class LightCrafter16Layout : public Node {
+ public:
+ static const char* name() { return "LightCrafter16"; }
+ static uint8_t dim() { return _2D; }
+ static const char* tags() { return "🚥"; }
+
+ bool pinsAreColumns = false;
+ uint16_t ledsPerPin = 10;
+
+ void setup() override {
+ addControl(pinsAreColumns, "pinsAreColumns", "checkbox");
+ addControl(ledsPerPin, "ledsPerPin", "number", 1, 2047);
+ }
+
+ void addStrip(uint16_t xposition, uint16_t start_y, uint16_t stop_y) {
+ bool increasing = start_y < stop_y;
+ for (int y = start_y; increasing ? (y <= stop_y) : (y >= stop_y); y += increasing ? 1 : -1) {
+ if (pinsAreColumns)
+ addLight(Coord3D(xposition, y, 0)); // limpkin
+ else
+ addLight(Coord3D(y, xposition, 0)); // ewowi
+ }
+
+ nextPin(); // each strip it's own pin
+ }
+
+ bool hasOnLayout() const override { return true; }
+ void onLayout() override {
+ addStrip(0, ledsPerPin - 1, 0);
+ addStrip(1, ledsPerPin - 1, 0);
+ addStrip(2, ledsPerPin - 1, 0);
+ addStrip(3, ledsPerPin - 1, 0);
+ addStrip(4, ledsPerPin - 1, 0);
+ addStrip(5, ledsPerPin - 1, 0);
+ addStrip(6, ledsPerPin - 1, 0);
+ addStrip(7, ledsPerPin - 1, 0);
+ addStrip(7, ledsPerPin, 2 * ledsPerPin - 1);
+ addStrip(6, ledsPerPin, 2 * ledsPerPin - 1);
+ addStrip(5, ledsPerPin, 2 * ledsPerPin - 1);
+ addStrip(4, ledsPerPin, 2 * ledsPerPin - 1);
+ addStrip(3, ledsPerPin, 2 * ledsPerPin - 1);
+ addStrip(2, ledsPerPin, 2 * ledsPerPin - 1);
+ addStrip(1, ledsPerPin, 2 * ledsPerPin - 1);
+ addStrip(0, ledsPerPin, 2 * ledsPerPin - 1);
+ }
+};
+
#endif // FT_MOONLIGHT
\ No newline at end of file