#include "ES32A08.h" /** * @brief Initializes the ES32A08 module. * * This function sets up the necessary pin modes and initial states for the * 74HC595D shift register (used for Relay and 7-Segment display), the power * LED, and the digital input shift register. It also configures the button * pins as input with pull-up resistors. Finally, it creates a task to update * the registers periodically. * * Pin configurations: * - DATA_PIN: Output for shift register data * - LATCH_PIN: Output for shift register latch * - CLOCK_PIN: Output for shift register clock * - OE_PIN: Output for shift register output enable * - PWR_LED_PIN: Output for power LED * - buttonPins[0-3]: Input with pull-up for buttons * - LOAD165_PIN: Output for digital input shift register load * - CLK165_PIN: Output for digital input shift register clock * - DATA165_PIN: Input for digital input shift register data * * The function also calls the reset() method to initialize the module state * and creates a FreeRTOS task to handle register updates. */ void ES32A08::begin() { // 74HC595D shift register config (Relay + 7-Seg disp.) pinMode(DATA_PIN, OUTPUT); pinMode(LATCH_PIN, OUTPUT); pinMode(CLOCK_PIN, OUTPUT); pinMode(OE_PIN, OUTPUT); digitalWrite(DATA_PIN, LOW); digitalWrite(LATCH_PIN, LOW); digitalWrite(CLOCK_PIN, LOW); digitalWrite(OE_PIN, LOW); pinMode(PWR_LED_PIN, OUTPUT); // "PWR" board Led pinMode(buttonPins[0], INPUT_PULLUP); pinMode(buttonPins[1], INPUT_PULLUP); pinMode(buttonPins[2], INPUT_PULLUP); pinMode(buttonPins[3], INPUT_PULLUP); // Digital inputs shift register config pinMode(LOAD165_PIN, OUTPUT); pinMode(CLK165_PIN, OUTPUT); pinMode(DATA165_PIN, INPUT); digitalWrite(LOAD165_PIN, HIGH); reset(); xTaskCreatePinnedToCore(ES32A08::updateRegisters, "Update Registers", 2048, // Stack size this, // Parameter 1, // Priority NULL, // Task handle 1 // Core on which the task will run ); } /** * @brief Resets the ES32A08 device to its initial state. * * This function performs the following actions: * - Turns on the power LED to indicate a reset state. * - Sets the current relay states to either all on or all off based on the RESET_RELAY_ON flag. * - Clears the display, which also triggers sending data to the shift register. */ void ES32A08::reset() { setPWRLED(RESET_LED_ON); currentRelays = RESET_RELAY_ON ? 0b11111111 : 0b00000000; clearDisplay(); // it also calls sendToShiftRegister() } /** * @brief Sends the current state of relays, digits, and segments to the shift registers. * * This function updates the shift registers with the current state of the relays, digits, * and segments by shifting out the data bits and latching the output. It controls three * shift registers, each handling 8 bits, for a total of 24 bits. * * @details * - The function first sets the latch pin to LOW to prepare for data transmission. * - It then shifts out the bits for the relays, digits, and segments in MSB first order. * - After shifting out the data, it sets the latch pin to HIGH to update the output. * - Finally, it introduces a small delay to ensure the data is properly latched. * * @note The delay is specified in microseconds and is defined by the DIGIT_PERS constant. */ void ES32A08::sendToShiftRegister() { digitalWrite(LATCH_PIN, LOW); shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, currentRelays); shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, currentDigits); shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, currentSegments); digitalWrite(LATCH_PIN, HIGH); delayMicroseconds(DIGIT_PERS); } // There are 3 shift registers for a total of 24 bits /** * @brief Clears the display buffer. * * This function sets all elements of the display buffer to zero, effectively * clearing the display. */ void ES32A08::clearDisplay() { memset(displayBuffer, 0, sizeof(displayBuffer)); } /** * @brief Updates the display registers continuously. * * This function runs an infinite loop that updates the display registers. * It iterates through the display buffer and updates the current digits and segments, * then sends the data to the shift register. * * @param instance A pointer to an instance of the ES32A08 class. */ void ES32A08::updateRegisters(void *instance) { ES32A08 *self = static_cast(instance); // Cast del puntatore for (;;) { for (uint8_t pos = 0; pos < 4; pos++) { self->currentDigits = self->digitNumber[1 + pos]; self->currentSegments = self->displayBuffer[pos]; self->sendToShiftRegister(); } } } /** * @brief Converts a character to its corresponding 7-segment display encoding. * * This function takes a character as input and returns the corresponding * 8-bit binary encoding for a 7-segment display. The encoding is based on * the common cathode 7-segment display configuration. * * @param c The character to be converted. Supported characters include: * '0'-'9', 'A'-'Z', 'a'-'z', ' ', '.', '-', '_'. * @return uint8_t The 8-bit binary encoding for the 7-segment display. * If the character is not recognized, it returns 0b00000000 (blank). */ uint8_t ES32A08::charToSegments(char c) { switch (c) { case '0': return 0b00111111; // 0 case '1': return 0b00000110; // 1 case '2': return 0b01011011; // 2 case '3': return 0b01001111; // 3 case '4': return 0b01100110; // 4 case '5': return 0b01101101; // 5 case '6': return 0b01111101; // 6 case '7': return 0b00000111; // 7 case '8': return 0b01111111; // 8 case '9': return 0b01101111; // 9 case ' ': return 0b00000000; // white space case '.': return 0b10000000; // decimal point case '-': return 0b01000000; // score case '_': return 0b00001000; // underscore case 'A': return 0b01110111; // A case 'a': return 0b01011111; // a case 'B': case 'b': return 0b01111100; // B-b case 'C': return 0b00111001; // C case 'c': return 0b01011000; // c case 'D': case 'd': return 0b01011110; // D-d case 'E': return 0b01111001; // E case 'e': return 0b01111011; // e case 'F': case 'f': return 0b01110001; // F-f case 'G': case 'g': return 0b01101111; // G-g case 'H': return 0b01110110; // H case 'h': return 0b01110100; // h case 'I': return 0b00000110; // I case 'i': return 0b00010000; // i case 'J': case 'j': return 0b00011110; // J-j case 'L': return 0b00111000; // L case 'l': return 0b00011000; // l case 'M': case 'm': return 0b00110111; // M-m case 'N': case 'n': return 0b01010100; // N-n case 'O': case 'o': return 0b01011100; // O-o case 'P': case 'p': return 0b01110011; // P-p case 'Q': case 'q': return 0b01100111; // Q-q case 'R': case 'r': return 0b01010000; // R-r case 'S': case 's': return 0b01101101; // S-s case 'T': case 't': return 0b01111000; // T-t case 'U': return 0b00111110; // U case 'u': return 0b00011100; // u case 'Y': case 'y': return 0b01100110; // Y-y case 'Z': case 'z': return 0b01011011; // Z-z default: return 0b00000000; // Blank if not found } } /** * @brief Displays a message on the ES32A08 display. * * This function clears the display buffer and then converts each character * in the provided message to its corresponding segment representation, * storing it in the display buffer. The message is truncated to a maximum * of 4 characters. * * @param message The message to be displayed. It should be a null-terminated * string with a maximum length of 4 characters. */ void ES32A08::display(const char *message) { memset(displayBuffer, 0, sizeof(displayBuffer)); for (int pos = 0; message[pos] != '\0' && pos < 4; pos++) displayBuffer[pos] = charToSegments(message[pos]); } /** * @brief Displays a given integer number on a 4-digit 7-segment display. * * This function takes an integer number and displays it on a 4-digit 7-segment display. * If the number is outside the range of -999 to 9999, it displays " -- ". * Otherwise, it formats the number to fit within 4 characters and updates the display buffer. * * @param number The integer number to be displayed. Valid range is -999 to 9999. */ void ES32A08::display(int number) { if (number > 9999 || number < -999) display(" -- "); else { char buffer[5]; // Chars buffer + endl ('\0') snprintf(buffer, sizeof(buffer), "%4d", number); for (int i = 0; i < 4; i++) displayBuffer[i] = charToSegments(buffer[i]); } } /** * @brief Displays a floating-point number on a 7-segment display. * * This function takes a floating-point number and converts it into a format * suitable for displaying on a 7-segment display. The number is formatted to * two decimal places. If the number is outside the range of -999 to 9999, * it displays " -- " to indicate an out-of-range value. * * @param number The floating-point number to be displayed. */ void ES32A08::display(float number) { memset(displayBuffer, 0, sizeof(displayBuffer)); if (number > 9999 || number < -999) display(" -- "); else { char buffer[6]; // Chars buffer + endl ('\0') snprintf(buffer, sizeof(buffer), "%.2f", number); int i; for (i = 0; i < 4 && buffer[i] != '.'; i++) { displayBuffer[i] = charToSegments(buffer[i]); } if (i == 4) return; // No decimal point found if (buffer[i] == '.') { displayBuffer[i - 1] |= 0b10000000; // Decimal point for (i; i < 5; i++) { displayBuffer[i] = charToSegments(buffer[i + 1]); } } } } /** * @brief Reads the analog current in milliamps from the specified channel. * * This function reads the analog value from the specified channel, converts it * to a current value in milliamps, and returns the result. The conversion is * based on a 12-bit ADC (Analog-to-Digital Converter) with a range of 0 to 4095. * * @param channel The analog input channel to read from. Valid values are 0 to 3. * @return The current in milliamps. If the channel is out of range, the function returns 0. */ float ES32A08::readAnalogmA(int channel) { if (channel < 0 || channel > 3) return 0; int adcValue = analogRead(mAInputPins[channel]); float current = ((adcValue / 4095.0) * (20.0 - 4.0)) * 2.0692; return current; } /** * @brief Reads the analog voltage from a specified channel. * * This function reads the analog value from the specified channel, converts it * to a voltage, and returns the result. The conversion assumes a 12-bit ADC * with a reference voltage of 10V and applies a scaling factor. * * @param channel The analog input channel to read from (valid values are 0 to 3). * @return The converted voltage value. Returns 0 if the channel is out of range. */ float ES32A08::readAnalogVoltage(int channel) { if (channel < 0 || channel > 3) return 0; int adcValue = analogRead(voltageInputPins[channel]); float voltage = ((adcValue / 4095.0) * 10.0) * 2.0692; return voltage; } /** * @brief Reads the raw analog voltage from the specified channel. * * This function reads the analog voltage from one of the predefined input pins * corresponding to the given channel. The channel must be between 0 and 3 inclusive. * If the channel is out of this range, the function returns 0. * * @param channel The analog input channel to read from (0-3). * @return The raw analog voltage value read from the specified channel, or 0 if the channel is invalid. */ int ES32A08::rawReadAnalogVoltage(int channel) { if (channel < 0 || channel > 3) return 0; return analogRead(voltageInputPins[channel]); } /** * @brief Sets the state of a specified relay. * * This function sets the state (on or off) of the relay specified by the * relay parameter. It updates the currentRelays variable and sends the * updated state to the shift register. * * @param relay The relay number to set (0-based index). * @param state The desired state of the relay (true for on, false for off). */ void ES32A08::setRelay(int relay, bool state) { bitWrite(currentRelays, relay, state); sendToShiftRegister(); } // Set all relays (0b00000000 - 0b11111111) /** * @brief Sets the state of the relays. * * This function updates the current state of the relays and sends the new state * to the shift register. * * @param relayStates An unsigned long representing the desired state of the relays. */ void ES32A08::setRelays(unsigned long relayStates) { currentRelays = relayStates; sendToShiftRegister(); } // Read all digital inputs /** * @brief Reads the digital inputs from a shift register. * * This function reads 8 digital inputs from a shift register connected to the * microcontroller. It first loads the inputs by toggling the LOAD165_PIN, then * reads each bit by toggling the CLK165_PIN and reading the DATA165_PIN. * * @return uint8_t A byte representing the state of the 8 digital inputs. */ uint8_t ES32A08::readDigitalInputs() { uint8_t inputs = 0; digitalWrite(LOAD165_PIN, LOW); delayMicroseconds(5); digitalWrite(LOAD165_PIN, HIGH); // delayMicroseconds(5); for (int i = 0; i < 8; i++) { digitalWrite(CLK165_PIN, LOW); // Init clock cycle delayMicroseconds(5); bitWrite(inputs, 7 - i, digitalRead(DATA165_PIN)); digitalWrite(CLK165_PIN, HIGH); // delayMicroseconds(5); } return inputs; } // Read single digital input /** * @brief Reads the state of a specified digital input. * * This function checks the state of a digital input specified by the input number. * The input number must be between 1 and 8, inclusive. If the input number is out * of this range, the function returns false. * * @param inputNumber The number of the digital input to read (1-8). * @return true if the specified digital input is high, false if it is low or if the input number is out of range. */ bool ES32A08::readDigitalInput(int inputNumber) { if (inputNumber < 1 || inputNumber > 8) return false; uint8_t digitalInputs = readDigitalInputs(); return digitalInputs & (1 << (inputNumber - 1)); } // Set "PWR" onboard LED /** * @brief Sets the state of the power LED. * * This function controls the power LED by setting its state to the specified value. * * @param state The desired state of the power LED. * - `true` to turn the LED off. * - `false` to turn the LED on. */ void ES32A08::setPWRLED(bool state) { digitalWrite(PWR_LED_PIN, !state); } // Read single button state /** * @brief Reads the state of a specified button. * * This function reads the digital state of a button specified by its number. * It returns `true` if the button is pressed and `false` otherwise. * * @param buttonNumber The number of the button to read (1-based index). * @return `true` if the button is pressed, `false` otherwise. */ bool ES32A08::readButton(int buttonNumber) { return !digitalRead(buttonPins[buttonNumber - 1]); }