Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions docs/moonbase/inputoutput.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
* 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)
17 changes: 14 additions & 3 deletions docs/moonlight/layouts.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) | <img width="320" alt="Single line" src="https://github.com/user-attachments/assets/70455279-646c-467d-b8e5-492b1aeae0fa" /> | |
| Single Row | ![Single row](https://github.com/user-attachments/assets/a88cea0f-9227-4da4-9a43-b944fd8bef97) | <img width="320" alt="Single row" src="https://github.com/user-attachments/assets/9f9918b9-e1ee-43a8-a02d-7f1ee182888b" /> | |
| SE16 | ![SE16](https://github.com/user-attachments/assets/45c7bec7-2386-4c42-8f24-5a57b87f0df9) | <img width="320" alt="SE16" src="https://github.com/user-attachments/assets/0efe941a-acf5-4a2c-a7d6-bdfa91574d1a" /> | Layout(s) including pins for Stephan Electronics 16-Pin ESP32-S3 board<br>see below |
| LightCrafter16 | ![LightCrafter16](https://github.com/user-attachments/assets/45c7bec7-2386-4c42-8f24-5a57b87f0df9) | <img width="320" alt="LightCrafter16" src="https://github.com/user-attachments/assets/0efe941a-acf5-4a2c-a7d6-bdfa91574d1a" /> | Layout(s) for Stephan Electronics LightCrafter16 ESP32-S3 board<br>see below |

!!! warning "Choosing pins"

Expand All @@ -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

<img width="320" alt="SE16" src="/firmware/installer/images/esp32-s3-stephanelec-16p.jpg"/>
![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.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified firmware/installer/images/esp32-s3-stephanelec-16p.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
81 changes: 74 additions & 7 deletions src/MoonBase/Modules/ModuleIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#if FT_MOONBASE == 1

#include "MoonBase/Module.h"
#include "driver/uart.h"

enum IO_PinUsageEnum {
pin_Unused, // 0
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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<JsonArray>()) {
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);
Expand All @@ -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
Expand Down
16 changes: 12 additions & 4 deletions src/MoonLight/Modules/ModuleDrivers.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,12 @@ class ModuleDrivers : public NodeManager {
addControlValue(control, getNameAndTags<IRDriver>());
addControlValue(control, getNameAndTags<HUB75Driver>());

// custom
addControlValue(control, getNameAndTags<SE16Layout>());
// board preset specific
_moduleIO->read([&](ModuleState& state) {
uint8_t boardPreset = state.data["boardPreset"];
if (boardPreset == board_SE16V1) addControlValue(control, getNameAndTags<SE16Layout>());
if (boardPreset == board_LightCrafter16) addControlValue(control, getNameAndTags<LightCrafter16Layout>());
});
}

Node* addNode(const uint8_t index, const char* name, const JsonArray& controls) const override {
Expand Down Expand Up @@ -128,8 +132,12 @@ class ModuleDrivers : public NodeManager {
if (!node) node = checkAndAlloc<IRDriver>(name);
if (!node) node = checkAndAlloc<HUB75Driver>(name);

// custom
if (!node) node = checkAndAlloc<SE16Layout>(name);
// board preset specific
_moduleIO->read([&](ModuleState& state) {
uint8_t boardPreset = state.data["boardPreset"];
if (!node && boardPreset == board_SE16V1) node = checkAndAlloc<SE16Layout>(name);
if (!node && boardPreset == board_LightCrafter16) node = checkAndAlloc<LightCrafter16Layout>(name);
});

#if FT_LIVESCRIPT
if (!node) {
Expand Down
48 changes: 48 additions & 0 deletions src/MoonLight/Nodes/Layouts/L_SE16.h
Original file line number Diff line number Diff line change
Expand Up @@ -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