Showing posts with label ESP-12. Show all posts
Showing posts with label ESP-12. Show all posts

Friday, April 21, 2017

ESPBasic Series: Web Interface Slider demo


How easy or complicated is in ESPBasic to have a instant response slider for your MPDMv4 AC Dimmer Web interface?

Just look at the code below:
cls
let xpv = 0
let PWMpin = 5
slider x,150,900
timer 500, [set.dimmer]
wprint "<br><br>"
button " Exit ", [TestExit]

wait

[set.dimmer]
 if x <> xpv then
   io(pwo,PWMpin,x)
   xpv = x
 end if
wait

[TestExit]
wprint |<a href="./edit"> Edit</a>|
end

 Yes, it's all there, plus some extras :)

The Power of ESPBasic !

Friday, February 24, 2017

ESPBasic Series : Decimal-to-Binary conversion


   I was looking a while ago to implement some Decimal-to-Binary conversion subroutine to be used for easy visualisation of the Registry content. Been able to see the values directly binary can help a lot when developping a driver for a IC or any piece of related software:


1. Decimal-to-Binary subroutine:
 [decbin]
  i = 1
  r = 0
  binval = 0
  nr = decval
 Do
   r = nr % 2
   nr = int(nr/2)
   binval = binval + r*i
   i = i*10
 Loop while nr > 0
wait

  2. Test Program:
decval = 0
binval = 0
wprint "<br><br>Decimal value "

textbox decval
button " Convert ", [decbin]

wprint "<br><br> Binary value "
textbox binval
wprint "<br><br>"
button " Exit ", [TestExit] 
Wait

[TestExit]
 Looking forward to see your own ideas about!

Happy breadboarding,
TJ.

Thursday, February 16, 2017

ESPBasic Series - DEC to BCD and BCD to DEC conversions


In the process of working on a new Driver Tutorial for the ESPBasic Series I am looking today to implement some Decimal-to-BCD and BCD-to-Decimal subroutines.

What I have done so far:

1. BCD-to-Decimal subroutine:
[DEC]
hv = val >> 4
hl = val and 15
val = hv*10 + hl
wait 
 2. Decimal-to-BCD subroutine:
[BCD]
d = int( val / 10 )
d1 = d * 10
d2 = val - d1
val = d*16 + d2
wait

 3. Test Program
let val = 96
textbox val
button "To DEC", [DEC]
button "To BCD", [BCD]
wprint "<br><br>"
button " Exit ", [TestExit]
wait

[TestExit]
end


If you have any other idea please feel free to share it, looking forward to see smarter solutions that that. I'm sure they are!

Happy breadboarding,
TJ.

Thursday, August 11, 2016

ESP8266 - Internal ADC 2 - the easy way example



This is just a very quick example on how to add a Voltage Divider to the ESP8266 Internal ADC input to increase the ADC input range.

For more theory behind, please take a look at the previous related article: ESP8266 - Internal ADC


Today project:

      Measure a voltage input range from 0-5V with the ESP8266 internal ADC


What do we need:

  • 2 Resistors for the voltage divider, R1=105.6k, R2=24.08k. I am using here precision resistors and the values are measured values with a proper calibrated bench meter.
  • a good, trustable, calibrated ok Multimeter.
  • some wires to connect all together.


I will not insist on connections, take a deeper look at the previous article about


Voltage divider schematic and Vout (ADC input voltage) formula




How do we do it :


1. Measure your Max desired input voltage
    In my case, Max Input Voltage (Vmax) = 5.1919 (measured)



 
2. Measure Resistors values:
    R1 = 105.6k
    R2 = 24.08k


 
3. Check if Full Scale Value at the voltage divider output is inside the ADC defined domain:
  • Calculated : Vout = (R2/(R1+R2))*Vin = 0.964072733 V
  • Measured = 0.96038V
     Good enough for the precision we are looking for. ESP8266 ADC is 10bit only and not exactly the most accurate in town



4. Calculate Voltage Divider Ratio:
  • Vdivider Ratio= Max Input Voltage/Fullscale value =  5.405976676



5.  Read ADC value : 
       adcr = adc.read(0)
      print("    ReadADC     : "..adcr)
     Average result for adcr = 1017

THIS IS IMPORTANT !! It tell us that we are inside the ADC domain as adcr < 1024 !

IF adcr > 1023 then you need to adjust your voltage divider resistors to fit inside ADC domain!



6. Calculate LSB  in 2 ways to cross check that we have the right value:
  • LSB = Input Voltage read by multimeter/ADC readed Value = 
                   =  5.1919 / 1017 =  0.005105113 V
      OR
  • LSB = (ADC Input  pin read Voltage by multimeter/ADC readed Value/)*Vdivider Ratio = 
                  = (0.96038/1017)*5.405976676 = 0.005105113 V
       It looks that we have the right LSB Value for our exercise!

     If you want to know also the ADC LSB, then
       LSB = ADC Input  pin read Voltage by multimeter/ADC readed Value  =   
                =  0.96038/1017 = 0.000944346 V

    Guess what's happening if you multiply ADC LSB with Vdivider ratio :)




7. Software Implementation for the ADC read function:

 function readADC()
      ad = 0
      LSB = 0.005105113 --calibrate based on your voltage divider AND Vref!
      adcr = adc.read(0)
      ad= adcr*LSB       
      print("    ReadADC     : "..adcr)
      print("    Read Voltage     : "..ad)
      return ad
end

and some results in the terminal window:

> SENT: readADC()
readADC()
    ReadADC     : 1017
    Read Voltage     : 5.191899921
>






Saturday, July 30, 2016

AC PWM Dimmer with Light Sensor



//YOUTUBE VIDEO HERE//

You can see this article as a continuation of the previous BH1750FVI - I2C Light Sensor Driver one.

    The main goal of the project is to create a AC Mains Light dimmer that can automatically control the level of light and keep in in the desired set interval without any external intervention.

   Why would you be intrested in such a setup? Well, if you have any interest in hydroponics, photography, or anywhere where you need some sort of constant flood light lamps with precise light intensitity level setup then might be a good idea to take a look :)


What we will need:

 

     Connection with the ESP8266 nEXT EVO Board is very easy, as MPU6050 GY-521 Module connector is fully compatible with the nEXT Bus connector. Depending on how to you choose you socket type, you can install it on TOP or Bottom of the ESP8266 nEXT EVO Board :



BH1750FVI Module directly connected to the ESP8266 nEXT EVO Board


And the whole project setup, with MPDVv4 AC Dimmer connected on ESP8266 GPIO13 (pin 7):

 
MPDM v4 + nEXT EVO + BH1750FVI



Closer look, MPDMv4 connected at ESP8266 GPIO13 (Pin7) for PWM control



Software implementation

You will need also the code from the previous BH1750FVI article. Please take also a look there for more details.




1. Ligth level auto-adjust function

 function set_light()
    read_input(dev_addr)
    ll = light - hst    --lower level
    lh = light + hst    --upper level
    if (lux < ll) then
       p=0
       i = i - step
       if (i<0) then i = 0
       end
       pwm.setduty(7, i)    
    end
    if (lux >lh) then
       p=0
       i = i + step
       if (i>880) then i = 880
       end
       pwm.setduty(7, i)    
    end
    --print("Auto Level Adjust : "..i)
    --print(ll)
    --print(lh)
    if (ll< lux and lux < lh) then
      if (p<1) then p=1
      end
      if (p<2) then
         print(string.format("\nLight Level: %0.2f lux",lux))
         print("Auto Level Adjust : "..i)
         p=2
      end
    end
end



Main Program
 
--PWM testing for MPDMv4 AC Dimmer Board
 

pwm.setup(7, 500, 850)    --MPDMv4 control pin
i=10                                  --initial Light Level

pwm.setduty(7, i)             --set initial ligfht level

light = 250                       --light level
hst = 40                            --histeresis coef for desired light interval
step = 1                            --light step
p=0         
 

--read light intensity value every 50 and autoadjust Light Dimmer Driver accordingly
tmr.alarm( 1, 50, 1, function()
  set_light()
end)



Wednesday, July 27, 2016

Mailbag - BH1750FVI - I2C Light Sensor Driver




//YOUTUBE VIDEO WILL FOLLOW HERE //

   In the process of choosing of some Light Sensors for one of the new projects that will involve the nEXT EVO Board and also the MPDMv4 Mains Dimmer  somebody suggested to give a try also to the ROHM BH1750FVI Sensor.

Just received some BH1750FVI modules in my mail yesterday and must say that it's a pretty impressive little board for only about 2.5USD, even less in a pack of 10!

This is how are looking the received modules:

BH1750FVI Module - TOP

   As you can see on the picture above, on the tiny board, you have the BH1750FVI Sensor, a LDO regulator some filtering caps and pull-up resistors and that's it!



 
BH1750FVI Module - Bottom
     The bottom side is bringing us more good news, the Module interface connector is fully compatible with the nEXT EVO  Bus!



BH1750FVI Overview

  BH1750FVI is an digital Ambient Light Sensor IC for I2C bus interface. This IC is the mostly used to obtain the ambient light data for adjusting LCD and Keypad backlight power of Mobile phone. 
 This is great as we are looking forward to use it for Automatic light adjustment using the MPDMv4 AC Dimmer board :)





Features

  • I2C bus Interface ( f / s Mode Support )
  • Spectral responsibility is approximately human eye response
  • Illuminance to Digital Converter
  • Wide range and High resolution. ( 1 - 65535 lx )
  • Low Current by power down function
  • 50Hz / 60Hz Light noise reject-function
  • 1.8V Logic input interface
  • Native 3.3V Device
  • No need any external parts
  • Light source dependency is little. ( ex. Incandescent Lamp. Fluorescent Lamp. Halogen Lamp. White LED. Sun Light )
  • It is possible to select 2 type of I2C slave-address.
  • Adjustable measurement result for influence of optical window ( It is possible to detect min. 0.11 lx, max. 100000 lx by using this function. )
  • Small measurement variation (+/- 20%)
  • The influence of infrared is very small.


Internal Diagram
BH1750FVI - Internal Diagram

  • PD - Photo diode with approximately human eye response.
  • AMP - Integration-OPAMP for converting from PD current to Voltage.
  • ADC - AD converter for obtainment Digital 16bit data.
  • Logic + I2C Interface :
                 - Ambient Light Calculation and I2C BUS Interface. It is including below register.
                 - Data Register - This is for registration of Ambient Light Data. Initial Value is
                       "0000_0000_0000_0000".
                 - Measurement Time Register - This is for registration of measurement time. Initial Value
                          is  "0100_0101".
  • OSC - Internal Oscillator ( typ. 320kHz ). It is CLK for internal logic.

For more details please take a look at the BH1750FVI Datasheet.



What we will need:

    Connection with the ESP8266 nEXT EVO Board is very easy, as MPU6050 GY-521 Module connector is fully compatible with the nEXT Bus connector. Depending on how to you choose you socket type, you can install it on TOP or Bottom of the ESP8266 nEXT EVO Board :


BH1750FVI Module directly connected to the ESP8266 nEXT EVO Board







BH1750FVI Driver implementation


1. Init I2C bus/interface


 Standard I2C Bus Initialisation function:

function init_I2C()
    i2c.setup(bus, sda, scl, i2c.SLOW)
end



2. WRITE Function - Set BH1750FVI Register config

      From BH1750FVI Datasheet :
   

  • No active state : pwr_down 0x00 
  • Wating for measurment command : pwr_on=0x01 
  • Reset data register value - not accepted in POWER_DOWN mode : reset=0x07

    Continuous modes, no pwr down:
  •  Start measurement at 1lx resolution. Measurement time is approx 120ms:
            hi_res_m1=0x10 
  • Start measurement at 0.5lx resolution. Measurement time is approx 120ms. 
           hi_res_m2=0x11
  • Start measurement at 4lx resolution. Measurement time is approx 16ms.
           lo_res_m1=0x13


    Device is automatically set to Power Down after measurement:
  • Start measurement at 1lx resolution. Measurement time is approx 120ms.
          hi_res_m3=0x20
  • Start measurement at 0.5lx resolution. Measurement time is approx 120ms.
           hi_res_m4=0x21
  • Start measurement at 1lx resolution. Measurement time is approx 120ms.
          lo_res_m2=0x23
 

function setcfg(cfg) 
    i2c.start(id)
    i2c.address(id, dev_addr ,i2c.TRANSMITTER)
    i2c.write(id,cfg)
    i2c.stop(id)
end


3. READ Sensor Input Function


Measurement Procedure

function read_input(dev_addr)
      i2c.start(id)
      i2c.address(id, dev_addr,i2c.RECEIVER)
      c = i2c.read(id,2)
      i2c.stop(id)
      --print("RAW H: "..string.byte(c,1))
      --print("RAW L: "..string.byte(c,2))
      rawl = (bit.lshift(string.byte(c, 1), 8) + string.byte(c, 2))
      lux = rawl/1.2
      print(string.format("\nLight Level: %0.2f lux",lux))
      --print(string.format("             %d raw",rawl))
      return rawl
end


4. MAIN PROGRAM


id = 0                    --I2C Bus ID
sda=2                     --GPIO4
scl=1                      --GPIO5
dev_addr = 0x23   --BH1750FVI I2C Address
i2c_init()                          --init I2C Bus
setcfg(hi_res_m1)            --SET config register
read_input(dev_addr)     --Read sensor data and compute LUX Light level value





Thursday, March 31, 2016

Mailbag - Si7021 / SHT21 Temperature/Humidity sensor








And the story behind: 

Somebody sent to me a nice & tiny temperature/humidity sensor breakout board based on the Si7021 IC:

Si7021 Module - Top View

On the Top side we have Si7021 Sensor only.


Si7021 Module - Bottom View

Bottom Side, a 3.3V LDO and a Voltage level shifting circuit that probably makes it 5V tolerant on I2C bus. We will use it at 3.3V so nothing to worry about.



DESCRIPTION

    The  Si7021  I2C  Humidity  and  Temperature  Sensor  is  a  monolithic  CMOS  IC integrating   humidity   and   temperature   sensor   elements,   an   analog-to-digital converter, signal processing, calibration data, and an I2C Interface.

    The patented use of industry-standard, low-K polymeric dielectrics for sensing humidity enables the  construction  of  low-power,  monolithic  CMOS  Sensor  ICs  with  low  drift  and hysteresis, and excellent long term stability.

   The  humidity  and  temperature  sensors  are  factory-calibrated  and  the  calibration data is stored in the on-chip non-volatile memory.  This ensures that the sensors are fully interchangeable, with no recalibration or software changes required.

   The Si7021 offers an accurate, low-power, factory-calibrated digital solution ideal for measuring humidity, dew-point, and temperature, in applications ranging from HVAC/R and asset tracking to industrial and consumer platforms.


Si7021 - Block Diagram


Nice. Looks more or less like SHT21 from Sensirion. And we will see that it is quite compatible (at least on the temp/humidity reading procedure side) with small differences for the rest of registers.




FEATURES
  • Precision Relative Humidity Sensor - ± 3% RH (max), 0–80% RH
  • High Accuracy Temperature Sensor - ±0.4 °C (max), –10 to 85 °C
  • 0 to 100% RH operating range
  • Up to –40 to +125 °C operating range
  • Wide operating voltage (1.9 to 3.6 V) (SHT21 - 2.1V min !)
  • Low Power Consumption:
         - 150 μA active current
         - 60 nA standby current
  • Factory-calibrated 
  • I2C Interface
  • Integrated on-chip heater
  • 3x3 mm DFN Package
  • Excellent long term stability
  • Optional factory-installed cover
        - Low-profile
        - Protection during reflow
        - Excludes liquids and particulates



Si7021 - Pinout Diagram



Typical Application Circuit for Relative Humidity and Temperature Measurements




For more details please take a look at the Si7021 Datasheet.



What we will need:

  • ESP8266 nEXT EVO Board
  • Si7021 Module as the one from above
  • For programming and uploading the driver and the software we will continue to use the LuaUploader as before.  
Connection with ESP8266 nEXT EVO Board is pretty straight-forward as the module is fully pin-to-pin compatible with the availavble nEXT Bus connector.

 
Si7021 Board connected with ESP8266 nEXT EVO DevBoard - TOP view




Si7021 Board connected with ESP8266 nEXT EVO DevBoard - 45 deg view





Software implementation

 The Si7021 communicates with the host controller over a digital I2C interface. The 7-bit base slave address is 0x40

 Master I2C devices communicate with the Si7021 using a command structure. The commands are listed below in the I2C command  table.  
 Commands  other  than  those  documented  below  are  undefined  and  should  not  be  sent  to  the device.


 
I2C Command Table



Issuing a Measurement Command
 
   The measurement commands instruct the Si7021 to perform one of two possible measurements: Relative Humidity or  Temperature. 

   The  procedure  to  issue  any  one  of  these  commands  is  identical.  While  the  measurement  is  in progress, the option of either clock stretching (Hold Master Mode) or Not Acknowledging read requests (No Hold Master  Mode)  is  available  to  indicate  to  the  master  that  the  measurement  is  in  progress. The  chosen  command code determines which mode is used.

   Optionally,  a  checksum  byte  can  be  returned  from  the  slave  for  use  in  checking  for  transmission  errors.  The checksum  byte  will  follow  the  least  significant  measurement  byte  if  it  is  acknowledged  by  the  master.  


   The checksum  byte  is  not  returned  if  the  master  “not  acknowledges”  the  least  significant  measurement  byte.  The checksum byte is calculated using a CRC generator polynomial of x^8+ x^5 + x^4 + 1, with an initialization of 0x00.

   The  checksum  byte  is  optional  after  initiating  an  RH  or  temperature  measurement  with  commands  0xE5,  0xF5,0xE3, and 0xF3. It is required for reading the electronic ID with commands 0xFA 0x0F and 0xFC 0xC9. 


  For all other commands, the checksum byte is not supported.









1. Init I2C bus/interface

 Standard I2C Bus Initialisation function:

function init_I2C()
    i2c.setup(bus, sda, scl, i2c.SLOW)
end

2.  Write Si7021 Register Function


    write_Si_Reg = function (dev_addr, set) 
          i2c.start(0x0)
          i2c.address(0x0, dev_addr ,i2c.TRANSMITTER)
          i2c.write(0x0,set)
          i2c.stop(0x0)
          tmr.delay(5000)
     end

 3.  Read Si7021 Register Function 


     read_Si_Reg = function (dev_addr)          
          i2c.start(0x0)
          i2c.address(0x0, dev_addr,i2c.RECEIVER)
          tmr.delay(5000)
          c = i2c.read(0x0,2)
          i2c.stop(0x0)

          rval = (bit.lshift(string.byte(c, 1), 8) + string.byte(c, 2))
          status = bit.band(rval,3)    --save status bits
          rval = bit.band(rval,65532)  --clear status bits
          return rval, status
     end

 4. Measuring Relative Humidity
 

         Once  a  relative  humidity  measurement  has  been  made,  the  results  of  the  measurement  
     may  be  converted  to percent relative humidity by using the following expression:

             hum = -6.0+125.0/65536.0*rval

       A humidity measurement will always return XXXXXX10 in the LSB field -> Status bit = 1 -> 

     marking a Humidity measurement data.

function read_hum()
   write_Si_Reg(dev_addr, RHumidityHoldCmd)
   tmr.delay(10000)
   read_Si_Reg(dev_addr)       
   hum = -6.0+125.0/65536.0*rval
   print("\nStatus : "..status)
   print("Humidity : "..string.format("%.2f",hum).."%")
end

 

5. Measuring Temperature

   Each time a relative humidity measurement is made a temperature measurement is also made for the purposes of temperature  compensation  of  the  relative  humidity  measurement.  If  the  temperature  value  is  required,  it  can  be read  using  command  0xE0;  this  avoids  having  to  perform  a  second  temperature  measurement.  


   The  measure temperature  commands  0xE3  and  0xF3  will  perform  a  temperature  measurement  and  return  the  measurement value, command 0xE0 does not perform a measurement but returns the temperature value measured during the relative humidity measurement.
The checksum output is not available with the 0xE0 command.

The results of the temperature measurement may be converted to temperature in degrees Celsius (°C) using the following expression:


     temp = -46.85+175.72/65536.0*rval

  A temperature measurement will always return XXXXXX00 in the LSB field - Status bit = 0 -> marking a Temperature measurement data.


 function read_temp()
   write_Si_Reg(dev_addr, TempHoldCmd)
   read_Si_Reg(dev_addr)      
   temp = -46.85+175.72/65536.0*rval
   print("Status : "..status)
   print("Temperature : "..string.format("%.2f",temp).."C")
end


6. Main Program
init_I2C()

tmr.alarm( 0, 5000, 1, function()
    read_hum()
    read_temp()
end)




Wednesday, March 30, 2016

ESP8266 - ADC Input frontend



Autorange Analog frontend


For a deeper Hardware description please take a look at the ESP8266 Analog Extension Board





What we will need:
 


ADC Frontend description

 Selection of the input voltage divider is done using an analog switch driven by PCF8574 PORT P0 and P1 bits:

0 - 1:20 Divider
1 - 1:10 Divider
2 - 1:5 Divider
3 - Full Voltage Range


As the ADC Input is programmed to be used in the 0-2V range that will give us the followings available ranges:

0 -> 0 - 40V
1 -> 0 - 20V
2 -> 0 - 10V
3 -> 0 - 2V

In the case of using the MCP3421 ADC at 12 Bit resolution, we will have the following corresponding LSB values:

- 1:20 Divider  -  0.02V
- 1:10 Divider  -  0.01V
- 1:5 Divider    -  0.005V
- Full Range    -  0.001V




Software implementation

1. Init I2C bus/interface

 Standard I2C Bus Initialisation function:

function init_I2C()
    i2c.setup(bus, sda, scl, i2c.SLOW)
end

 2. Set PCF8574 PORT Register Function
function setPort( port, stat) 
    i2c.start(id)
    i2c.address(id, dev_addr ,i2c.TRANSMITTER)
    i2c.write(id,stat)
    i2c.stop(id)
end

 3. Set Port function 

 Just a nicer way to write data to PCF8574 Register. Remember that we need to write a "ZERO" to the corresponding bit. We are sinking not sourcing !!

function setPortdata(p)
    pp = 255-p
    setPort(0x20,pp)
end


4. Set Voltage Divider function

  Select the desired Voltage divider ratio based on the choosen ratio value and calibrated LSB data.

--calibration data
x20 = 0.020056
x10 = 0.01014
x5  = 0.005014
xfl = 0.0010075
xrt = x20 --by default start with highest VDIV!
function SetVDivider(rtio)
    if (rtio==0) then xrt=x20 end
    if (rtio==1) then xrt=x10 end
    if (rtio==2) then xrt=x5 end
    if (rtio==3) then xrt=xfl end
    print("XRT = "..xrt)
    setPortdata(rtio)    -- select desired voltage divider
    return xrt
end



       -- Set/Change Voltage Divider ratio:
SetVDivider(0)  -- 1:20 Divider
SetVDivider(1)  -- 1:10 Divider
SetVDivider(2)  -- 1:5  Divider
SetVDivider(3)  -- 1:1  Full Voltage in!!


5. MAIN Program


-- Main Program
id = 0
sda=2 --GPIO4
scl=1 --GPIO5
dev_addr = 0x20
--calibration data
x20 = 0.020056
x10 = 0.01014
x5  = 0.005014
xfl = 0.0010075
xrt = x20 --by default start with highest VDIV!
--init I2C Bus
init_I2C()

--Init Volatage divider
setPortdata(0) -- --by default start with highest VDIV!
SetVDivider(0)  -- 1:20 Divider

----MCP3421 ADC
require('mcp3421')
sda=2 --GPIO4
scl=1 --GPIO5
mcp3421:init(sda, scl)
mcp3421:write_ADC_config(0x68, 0x10)

tmr.alarm( 0, 1000, 1, function()
    adc_val = mcp3421:read_ADC_data(0x68)
    print("\nADC Value : "..adc_val.." \n Voltage  : " ..adc_val*xrt)
    return adc_val
end)





Tuesday, February 23, 2016

MCP4728 - 12 Bit I2C DAC Driver - AN1



 MCP4726 - 12 Bit I2C DAC Driver for ESP8266 nEXT EVO AN-1


Youtube Video here


    From today we will move on the Analog interface part of the nEXT EVO Board AN-1 and we will start talking about the Digital to Analog conversion, Analog Autoscaling input and Analog to Digital conversion .

 For the Digital to Analog conversion part the choosen one is the Microchip MCP4728 I2C DAC IC.

 The MCP4728 device is a quad, 12-bit voltage output Digital-to-Analog Convertor (DAC) with non-volatile memory (EEPROM).

As it has a on-board precision output amplifier with rail-to-rail analog output swing capabilities that means first of all that we don't need any other Output Buffer as mandatory needed for non-buffered DAC's.

 The MCP4728 device has also a high precision internal voltage reference (VREF = 2.048V). The user can select the internal reference or external reference (VDD) for each channel individually.


Features

• 12-Bit Voltage Output DAC with 4 Buffered Voltage Outputs

        - Each output is driven by its own output buffer with a gain of 1 or 2 depending on the gain and
       VREF selection bit settings.
        - In normal mode, the DC impedance of the output pin is about 1Ω. In Power-Down mode,
       the output pin is internally connected to 1 kΩ, 100 kΩ, or 500 kΩ, depending on the
       Power-Down selection bit settings.
        - The VOUT pin can drive up to 1000 pF of capacitive load. It is recommended to use a load
       with RL greater than 5 kΩ.

• On-Board Non-Volatile Memory (EEPROM) for DAC Codes and I2CTM Address Bits
• Internal or External Voltage Reference Selection
• Output Voltage Range:
     - Using Internal VREF (2.048V):
            0.000V to 2.048V with Gain Setting = 1
           0.000V to 4.096V with Gain Setting = 2
     - Using External VREF (VDD): 0.000V to VDD
• ±0.2 LSB DNL (typical)
• Fast Settling Time: 6 μs (typical)
• Normal or Power-Down Mode
• Low Power Consumption
• Single-Supply Operation: 2.7V to 5.5V
• I2C Interface:
      - Address bits: User Programmable to EEPROM
      - Standard (100 kbps), Fast (400 kbps) and High Speed (3.4 Mbps) Modes
• 10-Lead MSOP Package
• Extended Temperature Range: -40°C to +125°C

If you want you can consider it the 4 channels big brother of the MCP4726 1 Channel 12 Bit DAC  

For more details, please see MCP4728 Datasheet



What we will need:
 
ESP8266 nEXT Evo + AN1 Board



Driver implementation
 
 
As MCP4728 has a I2C compatible compatible interface, building a driver for it it's a pretty straigh forward process:

 
1 . I2C Bus initialisation function
function  init_i2c(sda, scl)
          i2c.setup(id, sda, scl, i2c.SLOW)
     end

2. DAC Register Data load

 Each channel has its own volatile DAC input register and EEPROM. The details of the input registers and EEPROM are shown in the tables below:



  

2.1 Simple version using the Power supply Voltage as External 
      Voltage reference, Vref=Vcc

        -- single Write command version
 function dac(ch_reg,voltage)
          volt=(voltage*4096)/vcal -- calibrate!
          print("Voltage Steps:" .. string.format("%d",volt))

          msb = bit.rshift(volt, 8)
          print("MSB:" .. string.format("%d",msb))      
          lsb = volt-bit.lshift(msb,8)
          print("LSB:" .. string.format("%d",lsb))      

          i2c.start(id)
          i2c.address(id, dac_addr ,i2c.TRANSMITTER)
          i2c.write(id,ch_reg)
    
          i2c.write(id,msb)
          i2c.write(id,lsb)
          i2c.stop(id)
end


2.2 Using the internal Voltage Reference, Vref=2.048V
          - Gain settings also enabled, 1x,  2x
 function dac_vref(ch_reg,vref,g,voltage)
          volt=(voltage*4096)/vcal -- calibrate!
          print("Voltage Steps:" .. string.format("%d",volt))

          msb = bit.rshift(volt, 8)
          print("MSB:" .. string.format("%d",msb))      
          lsb = volt-bit.lshift(msb,8)
          print("LSB:" .. string.format("%d",lsb))      
          if (vref==1) then
                msb = msb + 128
                if (g==2) then msb = msb + 16 end
          end
          i2c.start(id)
          i2c.address(id, dac_addr ,i2c.TRANSMITTER)
          i2c.write(id,ch_reg)
    
          i2c.write(id,msb)
          i2c.write(id,lsb)
          i2c.stop(id)
end


 3. TEST program

3.1 Vref = External, Vref=Vcc


init_i2c(sda,scl)
--SINGLE WRITE COMMAND: WRITE A SINGLE DAC INPUT REGISTER AND EEPROM

ch_reg=0x58         -- CH A - VRef Vcc
dac(ch_reg,0.5)
ch_reg=0x5A        -- CH B - VRef Vcc
dac(ch_reg,1)
ch_reg=0x5C        -- CH C - VRef Vcc
dac(ch_reg,2)
ch_reg=0x5E        -- CH D - VRef Vcc
dac(ch_reg,3)

3.2 Vref = Internal, Vref=2.048V

 -- SET PG to x1 or x2 - valid only when Vref = Vref Internal !!
ch_reg = 0x58
vref= 1    --set internal Vref = 2.048 !!
vcal=2.048

gain=2
voltage = 1
voltage= voltage/2
dac_vref(ch_reg,vref,gain,voltage)
voltage=0.5
dac_vref(ch_reg,vref,gain,voltage)


 3.3 Vref = Vcc

-- EXTERNAL Vref = Vcc
voltage = 2
gain=1
ch_reg = 0x58
vref= 0 -- set EXTERNAL Vref = Vcc = 3.265 - MEASURE & Calibrate!!
vcal=3.265
dac_vref(ch_reg,vref,gain,voltage)

--autoupdate DAC every sec
tmr.alarm( 0, 1000, 1, function()
  print("\nUpdate DAC data")
  dac_vref(ch_reg,vref,gain,voltage)
end)