Home Assistant devices and uses

Thread Starter

nsaspook

Joined Aug 27, 2009
16,249
The eve power plugs with thread matter support work perfectly with the latest HA release. On/off control and power/energy report directly from the matter profile. No remote network or cloud needed. I have two testing units to monitor Aircon usage in HA.
PXL_20250416_214252071.jpg


https://www.evehome.com/en/eve-energy-works-with-home-assistant

With the firmware update released today, Eve Energy is laying the foundation for manufacturer-independent energy management in Matter. The plug’s previous, proprietary methodology is being replaced by the reporting functions specified in Matter, which Eve helped to develop as a member of the Connectivity Standards Alliance. The aim of this standardization was to create an open interface for the collaboration of devices from a wide variety of sources. On platforms that follow the example of Home Assistant and natively support energy reporting, plugs such as Eve Energy will provide real-time data such as power, voltage and amperage as well as cumulative power consumption values, enabling dynamic and automatic device control, based on this data. What’s more, the new firmware also improves the robustness of Eve Energy, especially in large Thread networks.
 

Thread Starter

nsaspook

Joined Aug 27, 2009
16,249
Some Home Assistant data screens of the two Aircon data monitors using the Eve Energy devices. I love these things and will buy more.
1745011751798.png
1745011774518.png
HT monitor, OFF
1745011853055.png
A bedroom monitor, RUNNING
 

Thread Starter

nsaspook

Joined Aug 27, 2009
16,249
Made a few remote PV voltage monitors using the Orange PI 3 zero 3 4G running Debian bookworm Linux.

Modified some old USB interfaces to handle up to 80+ volts by adding a resistive voltage divider to each ADC input to the frontend.
1745517312981.png
1745517707502.png
These little boxes are nice, Fan and heat sinks for remote operation in heat.
1745517729470.png
Connected to the main PV disconnect in the remote work shed.

1745517363850.png

1745517810196.png

Pulled down the latest Linux source tree for the OPI computers and modified the Linux source to add some drivers for the USB DAQ devices and to optimize the kernel for low latency near RT operation. Wrote a user application that uses that driver
to pull the ADC data, buffer, average and over-sample the raw results into the needed voltage units. Calibrated with my Fluke using constants selected for each board using the Linux machines IP address.

https://github.com/nsaspook/mqtt_comedi
C:
#define HV_SCALE0        83.6f
#define HV_SCALE1        74.4f
#define HV_SCALE2        83.6f
#define HV_SCALE3        83.6f

        struct ha_daq_hosts_type {
                const char hosts[4][NI_MAXHOST];
                const char clients[4][NI_MAXHOST];
                char hname[4][NI_MAXHOST];
                double scaler[4];
                uint8_t hindex;
        };

struct ha_daq_hosts_type ha_daq_host = {
    .hosts[0] = "10.1.1.30",
    .hosts[1] = "10.1.1.39",
    .hosts[2] = "10.1.2.30",
    .hosts[3] = "10.1.2.39",
    .hname[0] = "RDAQ1",
    .hname[1] = "RDAQ2",
    .hname[2] = "RDAQ3",
    .hname[3] = "RDAQ4",
    .clients[0] = "Energy_Mqtt_BMC1",
    .clients[1] = "Energy_Mqtt_BMC2",
    .clients[2] = "Energy_Mqtt_BMC3",
    .clients[3] = "Energy_Mqtt_BMC4",
    .scaler[0] = HV_SCALE0,
    .scaler[1] = HV_SCALE1,
    .scaler[2] = HV_SCALE2,
    .scaler[3] = HV_SCALE3,
    .hindex = 0,
};

        if (ifa->ifa_addr->sa_family == AF_INET) {
            if (s != 0) {
                exit(EXIT_FAILURE);
            }
            printf("\tInterface : <%s>\n", ifa->ifa_name);
            printf("\t  Address : <%s>\n", host);

            if (strcmp(host, &ha_daq_host.hosts[0][0]) == 0) {
                ha_daq_host.hindex = 0;
            }
            if (strcmp(host, &ha_daq_host.hosts[1][0]) == 0) {
                ha_daq_host.hindex = 1;
            }
        }
       
#define OVER_SAMP       16

    over_sample = 0.0f; // over-sample avg
    for (int i = 0; i < OVER_SAMP; i++) {
        over_sample += ac0_filter(get_adc_volts(0));
    }
    E.adc[0] = over_sample / (double) OVER_SAMP;

    over_sample = 0.0f; // over-sample avg
    for (int i = 0; i < OVER_SAMP; i++) {
        over_sample += ac1_filter(get_adc_volts(1));
    }
    E.adc[1] = over_sample / (double) OVER_SAMP;
The program then converts that data in JSON format and sends it via MQTT to the central server.

1745518303107.png

C:
    json = cJSON_CreateObject();
    strncpy(&ha_daq_host.hname[ha_daq_host.hindex][5], "name", 64);
    cJSON_AddStringToObject(json, (const char *) &ha_daq_host.hname[ha_daq_host.hindex], (const char *) &ha_daq_host.clients[ha_daq_host.hindex]);
    strncpy(&ha_daq_host.hname[ha_daq_host.hindex][5], "sequence", 64);
    cJSON_AddNumberToObject(json, (const char *) &ha_daq_host.hname[ha_daq_host.hindex], E.sequence);
    strncpy(&ha_daq_host.hname[ha_daq_host.hindex][5], "mqtt_do_16b", 64);
    cJSON_AddNumberToObject(json, (const char *) &ha_daq_host.hname[ha_daq_host.hindex], (double) E.do_16b);
    strncpy(&ha_daq_host.hname[ha_daq_host.hindex][5], "mqtt_di_16b", 64);
    cJSON_AddNumberToObject(json, (const char *) &ha_daq_host.hname[ha_daq_host.hindex], (double) E.di_16b);
    strncpy(&ha_daq_host.hname[ha_daq_host.hindex][5], "bmc_adc0", 64);
    cJSON_AddNumberToObject(json, (const char *) &ha_daq_host.hname[ha_daq_host.hindex], E.adc[0]);
    strncpy(&ha_daq_host.hname[ha_daq_host.hindex][5], "bmc_adc1", 64);
    cJSON_AddNumberToObject(json, (const char *) &ha_daq_host.hname[ha_daq_host.hindex], E.adc[1]);
    strncpy(&ha_daq_host.hname[ha_daq_host.hindex][5], "build_date", 64);
    cJSON_AddStringToObject(json, (const char *) &ha_daq_host.hname[ha_daq_host.hindex], FW_Date);
    strncpy(&ha_daq_host.hname[ha_daq_host.hindex][5], "build_time", 64);
    cJSON_AddStringToObject(json, (const char *) &ha_daq_host.hname[ha_daq_host.hindex], FW_Time);
    time(&rawtime);
    strncpy(&ha_daq_host.hname[ha_daq_host.hindex][5], "sequence_time", 64);
    cJSON_AddNumberToObject(json, (const char *) &ha_daq_host.hname[ha_daq_host.hindex], (double) rawtime);
    // convert the cJSON object to a JSON string
    char *json_str = cJSON_Print(json);

    pubmsg.payload = json_str;
    pubmsg.payloadlen = strlen(json_str);
    pubmsg.qos = QOS;
    pubmsg.retained = 0;

    MQTTClient_publishMessage(client_p, topic_p, &pubmsg, &token);
The Home Assistant system them reads that MQTT data and logs it into the energy database for logging and monitoring for possible actions.

1745518682344.png
With this system in place I can monitor the voltage drops from the remote PV feed point on the buried DC interconnect cable and the local PV array voltages. Looks to be about a volt loss. Could be better but I'm getting too old to trench in another cable.


1745518805696.png
Yellow: Remote feed voltage
Blue: In home GTI inverter voltage. With this data I can calculate the line power loss and check for possible loose connections or switching problems that caused this from a bent contact pulling near 30A from one of the arrays.

1745519021088.png

A new switch has been installed and qualified at full power for a week.

1745519155077.png
 
Last edited:

Thread Starter

nsaspook

Joined Aug 27, 2009
16,249
Designing a modern replacement for those old USB DAQ devices for connection to the SPI bus on the Orange PI. It will be much faster and a lot more capable.

Using one of my Q84 serial boards as a testing platform for SPI master to sequence the SPI slave on the same controller.
1746070943538.png
1746071315813.png

The Master SPI send a request for ADC channel 1 and the Slave device responds with the measured value.
1746071099423.png
Some testing software:
https://github.com/nsaspook/mqtt_comedi/tree/main/Q84/bmc_slave.X

C:
int8_t test_slave(void)
{
    static uint8_t ret = 0, i = 0;

    DLED_SetHigh();
    wait_lcd_done();
    CS_SetHigh();
    SPI2CON0bits.EN = 1;

    wait_lcd_done();
    if (i++ == 4) {
        send_spi2_data_dma(CMD_ADC_GO + 1, CMD_ADC_DATA, CMD_ADC_DATA, 3);
//        send_spi2_data_dma(CMD_ADC_GO + 3, CMD_ADC_DATA, CMD_ADC_DATA, 3);
//        send_spi2_data_dma(CMD_ADC_GO + 2, CMD_ADC_DATA, CMD_ADC_DATA, 3);
        i = 0;
    }
    wait_lcd_done();
    ret = SPI1_ReadByte();
    serial_buffer_ss.data[3] = ret;
    SPI2CON0bits.EN = 0;

    return(int8_t) ret;
}
All of the slave SPI code runs in interrupts triggered from the Slave MOSI or responses MISO.

C:
void slaveo_adc_isr(void)
{
    PIR1bits.ADIF = LOW;
    spi_stat_ss.adc_count++; // just keep count
    adc_buffer[channel] = (uint16_t) ADRES; // data is ready but must be written to the SPI buffer before a master command is received
    serial_buffer_ss.adch = ADRESH;
    serial_buffer_ss.adcl = ADRESL;
    spi_comm_ss.ADC_DATA = true; // so the transmit buffer will not be overwritten
    spi_comm_ss.ADC_RUN = false;
    flipper = true;
}

void slaveo_tx_isr(void)
{
    MLED_SetHigh();
    spi_stat_ss.slave_tx_count++;
    if (flipper) {
        SPI2TXB = serial_buffer_ss.adch;
        SPI2TXB = serial_buffer_ss.adch;
        SPI2TXB = serial_buffer_ss.adch;
    } else {
        SPI2TXB = serial_buffer_ss.adcl;
        SPI2TXB = serial_buffer_ss.adcl;
        SPI2TXB = serial_buffer_ss.adcl;
    }
    flipper = !flipper;
    MLED_SetLow();
}
with a simple command parser in the receive ISR.

C:
void slaveo_rx_isr(void)
{
    uint8_t command, char_rxtmp, char_txtmp, cmd_dummy = CMD_DUMMY;
    /* we only get this when the master wants data, the slave never generates one */
    // SPI port #2 SLAVE receiver
    spi_stat_ss.slave_int_count++;
    data_in2 = SPI2RXB;
    serial_buffer_ss.data[0] = data_in2;
    command = data_in2 & HI_NIBBLE;
    serial_buffer_ss.command = command;

    if (UART1_is_rx_ready()) { // we need to read the buffer in sync with the *_CHAR_* commands so it's polled
        char_rxtmp = UART1_Read();
        serial_buffer_ss.data[1] = char_rxtmp;
        cmd_dummy |= UART_DUMMY_MASK; // We have real USART data waiting
        spi_comm_ss.CHAR_DATA = true;
    }

    if (command == CMD_CHAR_GO) {
        char_txtmp = (data_in2 & LO_NIBBLE); // read lower 4 bits
        //        serial_buffer_ss.tx_buffer = char_rxtmp;
        spi_stat_ss.char_count++;
        MLED_Toggle();
    }

    if (command == CMD_CHAR_DATA) { // get upper 4 bits send bits and send the data
        if (UART1_is_tx_ready()) { // The USART send buffer is ready
            UART1_Write(((data_in2 & LO_NIBBLE) << 4) | char_txtmp);
        } else {
        }
        serial_buffer_ss.tx_buffer = cmd_dummy;
        cmd_dummy = CMD_DUMMY; // clear rx bit
        spi_comm_ss.CHAR_DATA = false;
        spi_comm_ss.REMOTE_LINK = true;
        /* reset link data timer if we are talking */
        TMR0_Reload();
    }

    if (command == CMD_ADC_GO) { // Found a GO for a conversion command
        spi_comm_ss.ADC_RUN = true;
        spi_stat_ss.adc_count++;

        channel = data_in2 & LO_NIBBLE;
        if (channel > 7) channel = 0; // invalid so set to 0
        if (!ADCON0bits.GO) {
            ADPCH = channel;
            adc_buffer[channel] = 0xffff; // fill with bits
            ADCON0bits.GO = HIGH; // start a conversion
        } else {
            ADCON0bits.GO = LOW; // stop a conversion
            spi_stat_ss.adc_error_count++;
        }
        spi_comm_ss.REMOTE_LINK = true;
        while (!SPI2STATUSbits.RXRE) { // clear the FIFO of data
            data_in2 = SPI2RXB;
        }
        SPI2STATUSbits.RXRE = 0;
        TMR0_Reload();
    }

    if (data_in2 == CMD_ADC_DATA) {
        spi_stat_ss.last_slave_int_count = spi_stat_ss.slave_int_count;
    }

    if (command == CMD_CHAR_RX) {
        serial_buffer_ss.tx_buffer = char_rxtmp;
        cmd_dummy = CMD_DUMMY; // clear rx bit
    }

}
Works to the point of validating the hardware design. Will rewrite the code when I assemble the first test board.

1746071286316.png
The host master will have access to the Q84 controller resources and two other devices on the DAQ board.
https://www.ti.com/lit/ds/symlink/tic12400.pdf
TIC12400 24-Input Multiple Switch Detection Interface (MSDI) Device
With Integrated ADC and Adjustable Wetting Current

https://www.mouser.com/datasheet/2/302/MC33996-3138519.pdf
16 Output Switch with SPI Control

I've used those chips before on the MCP2210 board.
https://forum.allaboutcircuits.com/...2210-on-linux-with-libusb.179080/post-1694189
PXL_20211227_212126290.jpg

I ordered the prototype boards for this from JCL. Hope they get here before the China shutdown.
 
Last edited:

wayneh

Joined Sep 9, 2010
18,087
I sleep good physically, I learned the power of directed dreams long ago to solve problems. I work on solutions to problems in my dreams, the body rests but the brain rarely takes a holiday.
So true. I learned long ago that when you're beating your head against a wall on a project, step away. Go do something else, take a run, whatever. It'll be 30 minutes into a run when it dawns on you that the stuck bolt you've been breaking your knuckles to turn is a left-handed thread. Duh!

Coding into the late hours may feel like progress, but so often in the light of day the problem becomes so much simpler and your late-night code gets replaced by a few lines of elegance.
 

Thread Starter

nsaspook

Joined Aug 27, 2009
16,249
Time to create a Orange PI daq driver for the SPI connection to the new DAQ board. I made a previous RPi that was similar so I used that as the starting point for the new protocol driver designed for the new board.
https://github.com/nsaspook/mqtt_comedi

Using the Linux Comedi API means I can reuse the program that currently using the USB interface with a Comedi driver for for DAQ without changes without changes of the source code.
https://www.comedi.org/

Orange PI zero 3 board with SPI connected for testing it as a SPI master.
1746560434385.png
1746560464126.png
SPI master with ADC data commands for the Q84 device. I have the option set to toggle CS between each transmission. That come be switched off.
1746560553026.png
1746561358984.png
C:
#ifndef DIGITAL_ONLY
    over_sample = 0.0f; // over-sample avg
    for (int i = 0; i < OVER_SAMP; i++) {
        over_sample += ac0_filter(get_adc_volts(0));
    }
    E.adc[0] = over_sample / (double) OVER_SAMP;

    over_sample = 0.0f; // over-sample avg
    for (int i = 0; i < OVER_SAMP; i++) {
        over_sample += ac1_filter(get_adc_volts(1));
    }
    E.adc[1] = over_sample / (double) OVER_SAMP;

#ifdef DAC_TESTING
get_adc_volts is called over and over for oversampling and averaging the DC voltages. The Master MISO input is shorted to common to send zero volts per each sample for testing. With no board connected the voltage would be 80+ volts vs the normal board measured voltage of less than 40VDC open circuit from the panels.

Boot log of the comedi and spibmc drivers loading using device tree configuration file.
1746560944971.png

DHL says my boards are on the way. I hope there are not too many hardware fixes needed on the PCB, as China is expensive today, even for unpopulated boards.
 

Thread Starter

nsaspook

Joined Aug 27, 2009
16,249
Boards are here and this is the first boot on the controller part of that PCB.
1746740872774.png
1746740894753.png
Not ready to solder them together so just a hand pinch to connect the pins with power on.

1746740916333.png
Sweet but I expected that part to be good. Just noticed I'm short of 1K 805 resistors. Time to make a parts run from work. ;)
 

Thread Starter

nsaspook

Joined Aug 27, 2009
16,249
1746808356747.png
1746808378832.png
Just about fully populated but I'm missing the SMD version of some DC voltage divider Zener protection Z1 and Z2 diodes. I know I have them but can't find them.

Only one small problem so far. The 4.096V ADC reference was reading near zero. I rechecked the PCB design but it was good. The problem? I have the wrong chip in stock. I needed the T version but installed and only have the the non-T version.

A few solder blobs and a tiny jump fixed that. Will order the correct ones. :p
1746808828977.png
1746808871811.png
Nice Vref.
1746808902202.png
Easy fix because I didn't ground the NC pins on the PCB.
 

Thread Starter

nsaspook

Joined Aug 27, 2009
16,249
All of the basic ADC, DI and DO functionality is working on the DAQ board firmware connected to the orange PI host running the kernel driver.
Blink (binary counting up) LED testing the 16-bit DO chip.
1747435396118.png
1747435421648.png
Time to sweep the into another board REV with fixes and improvements.
Time to clean up the software too.
1747435492973.png
1747435511459.png

1747435608642.png
Time for a little food.
 

Thread Starter

nsaspook

Joined Aug 27, 2009
16,249
1747855575755.png
Waiting for the new PCB from JLC.
1747852327588.png
PIC18F47Q84 Firmware main dependency -graph using XC8 and MPLABX.

1747852958795.png
1747853243476.png
DIO driver section, SPI2 handles the Orange Pi command and data Master, SPI1 handles the CLCD display, mc33996 and tic12400 Slaves. Data Buffers handle I/O between SPI2 and SPI1 for OPi Comedi DAQ requests from the test program.



Linux ARM64 GCC Allwinner H618 https://linux-sunxi.org/Xunlong_Orange_Pi_Zero3
1747854280427.png
1747852570738.png
1747853471709.png
1747853767625.png
BMCBoard Linux test program functions dependency-graphs.
 

Thread Starter

nsaspook

Joined Aug 27, 2009
16,249
1748366335405.png
1748366423821.png
1748359717554.png

New board should be here this week.
Have the DAQ function looking pretty good with fairly stable sampling times from the OPi to the Q84. Lots of switching hash on the ~4Hz 256 step signal.
1748359828048.png
1748359884672.png
1748359909184.png
 
Last edited:

Thread Starter

nsaspook

Joined Aug 27, 2009
16,249
The real trick is time, keeping things quick.. The complete Q84 Slave SPI functionality runs in a ISR triggered by the SPI receive interrupt, with display and DAQ happening in the main process loop.

The first thing is to set the ISR/DMA/MAIN priority with memory and CPU resources on the Q84 with main I/O and ISR processing of link requests for gets and puts to various devices at code startup. A volatile memory buffer isolates the async processes.
C:
/*
* ISR Priority > Peripheral Priority > Main Priority
* '0' being the highest priority selection and the maximum value being the lowest priority
*/
void SetBMCPriority(void)
{
    INTCON0bits.GIE = 0; // Disable Interrupts;
    PRLOCK = 0x55;
    PRLOCK = 0xAA;
    PRLOCKbits.PRLOCKED = 0; // Allow changing priority settings;
    INTCON0bits.GIE = 1; // Enable Interrupts;
    ISRPR = 1;
    DMA1PR = 2;
    MAINPR = 7;
    INTCON0bits.GIE = 0; // Disable Interrupts;
    PRLOCK = 0x55;
    PRLOCK = 0xAA;
    PRLOCKbits.PRLOCKED = 1; // Grant memory access to peripherals;
    INTCON0bits.GIE = 1; // Enable Interrupts;
}
The the actual slave SPI processing code must be as efficient as possible to keep the ISR processing time under the time it takes between incoming SPI transactions, 40us on average using the receive/transmit 2-byte FIFO on the Q84.

1748370871794.png

SPI data stream from to the the OPi. Line 3 are the SPI clocks, line 2: the CS for the Q84 slave SPI. A OPi link command is sent first, then 4 byes of data and responses from the slave SPI module.
C:
/*
* PIC Slave commands
*/
static const uint8_t CMD_ZERO = 0x00;
static const uint8_t CMD_ADC_GO = 0x80;
static const uint8_t CMD_DAC_GO = 0x90;
static const uint8_t CMD_PORT_GO = 0xa0; /* send data to port output DO buffer */
static const uint8_t CMD_CHAR_GO = 0xb0;
static const uint8_t CMD_ADC_DATA = 0xc0;
static const uint8_t CMD_PORT_DATA = 0xd0;
static const uint8_t CMD_CHAR_DATA = 0xe0;
static const uint8_t CMD_PORT_GET = 0xf0; /* read data from input DI buffer */
static const uint8_t CMD_CHAR_RX = 0x10; /* Get RX buffer */
static const uint8_t CMD_DUMMY_CFG = 0x40; /* stuff config data in SPI buffer */
static const uint8_t CMD_DEAD = 0xff; /* This is usually a bad response */
0x90 for a DAC command data push to the Q84. As is usual for SPI, the next packet mirrors the last slave received data back to the host.

1748371619148.png

The Q84 only has a 8-bit DAC, so only one byte from the receiver buffer is sent to the async data buffer. V.bmc_ao

Select parts of the full ISR. Each type of DAQ command has a command and packet routine.
C:
void slaveo_rx_isr(void)
{
    uint8_t command, char_rxtmp, char_txtmp, cmd_dummy = CMD_DUMMY;

    /* we only get this when the master wants data, the slave never generates one */
    // SPI port #2 SLAVE receiver

    DEBUG1_SetHigh();



#define PORT_GET_BYTES    4 // data packet in bytes
#define ADC_GET_BYTES    3
#define PORT_GO_BYTES    3
#define DAC_GO_BYTES    3

    // DAC_GO_BYTES
    if (serial_buffer_ss.dac_value) {
        if (serial_buffer_ss.raw_index == DAC_GO_BYTES) {
            V.bmc_ao = serial_buffer_ss.data[1];
            serial_buffer_ss.dac_value = false;
            serial_buffer_ss.raw_index = 0;
            spi_stat_ss.txdone_bit++; // number of completed packets
            spi_stat_ss.slave_tx_count++;
            data_in2 = 0;
        } else {
            spi_stat_ss.slave_tx_count++;
            data_in2 = 0;
        }
    }

    if (++serial_buffer_ss.raw_index > 8) {
        serial_buffer_ss.raw_index = 0;
        spi_stat_ss.txuf_bit++; // buffer high watermark cleared
        MLED_SetHigh();
    }

    command = data_in2 & HI_NIBBLE;

    if (serial_buffer_ss.dac_value || serial_buffer_ss.get_value || serial_buffer_ss.make_value || serial_buffer_ss.dac_value) {
        goto isr_end;
    }

    if (command == CMD_DAC_GO) { // Found a GO for a DAC conversion command
        spi_comm_ss.ADC_RUN = false;
        spi_comm_ss.PORT_DATA = false;
        spi_stat_ss.dac_count++;
        serial_buffer_ss.raw_index = 1;
        serial_buffer_ss.dac_value = true;
        spi_stat_ss.slave_tx_count++;
        spi_comm_ss.REMOTE_LINK = true;
        TMR0_Reload();
    }

isr_end:
    spi_stat_ss.slave_int_count++;
    MLED_SetLow();
    DEBUG1_SetLow();
The DEBUG1 GPIO time in ISR trace. Worse case is 20us with a 10us average in ISR processing time Z 10.0us zoomed trace from DAQ requests from the OPi.

1748372407005.png
Q84 Slave ISR processing timing.

Why it's nice to have several scopes for embedded programming.
1748373098522.png
You can look for timing glitches easily.
 
Last edited:

Thread Starter

nsaspook

Joined Aug 27, 2009
16,249
New board is up and running, no issues so far. Doing some end to end signal testing from the OPi to Q84 and back to the OPi for possible issues with the SPI protocol. Trying to see that breaks first and what countermeasures are needed.
A simple LED flasher.

Diagnostic screen that normally would be a static boot message in run mode.

Orange Pi test flasher program sending remote commands to the Q84 board via SPI link.
1748535787716.png

1748535888255.png
 

Thread Starter

nsaspook

Joined Aug 27, 2009
16,249
Finally finishing up the ADC end to end calibration and stability testing.

Voltage divider and direct channel raw 12-bit readbacks.

1748886105896.png
Left to Right, Top to bottom.
A0, A1, A2, A4
A5, C6, C7, D5


1748887310546.png
A1 and A2 are the RS232 Mark Space reference, shifted and scaled , on the wire voltages +-12VDC. No need for calibration.


1748886556095.png
1748886247769.png
1748886409006.png
A4 and A5 20VDC calibration after scaling the ADC values to the needed units.
 
Last edited:

Thread Starter

nsaspook

Joined Aug 27, 2009
16,249
1749270766221.png
I have couple of the DAQ units online for operational test. The little OPi Linux servers can run a complete HA system with Matter device control using the Ethernet or wireless connection.

1749270793158.png
Stripped Q84 only version for 12-bit ADC and 8-bit DAC applications not needing 24vdc I/O. The Linux driver queries the hardware for what devices are on the board.

1749271240988.png

1749270812359.png
 
Top