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 -![SE-16p](../firmware/installer/images/esp32-s3-stephanelec-16p.jpg){: style="width:100px"} +![SE-16p](../firmware/installer/images/esp32-s3-stephanelec-16p.jpg) * 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 + +![SE-16p](../firmware/installer/images/esp32-s3-lightcrafter16.jpg) + +* 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 line](https://github.com/user-attachments/assets/4ba5a3ac-9312-4bac-876d-cfa3dce41215) | Single line | | | Single Row | ![Single row](https://github.com/user-attachments/assets/a88cea0f-9227-4da4-9a43-b944fd8bef97) | Single row | | | SE16 | ![SE16](https://github.com/user-attachments/assets/45c7bec7-2386-4c42-8f24-5a57b87f0df9) | SE16 | Layout(s) including pins for Stephan Electronics 16-Pin ESP32-S3 board
see below | +| LightCrafter16 | ![LightCrafter16](https://github.com/user-attachments/assets/45c7bec7-2386-4c42-8f24-5a57b87f0df9) | 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 -SE16 +![SE-16p](../firmware/installer/images/esp32-s3-stephanelec-16p.jpg) * 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 + +![SE-16p](../firmware/installer/images/esp32-s3-lightcrafter16.jpg) + +* 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