Closed sunshine-nick closed 2 years ago
Excellent idea!
dac_cw_config_t dac_cw_config;
bool leftright = true; // simple way to demonstrate left or right channels uint32_t frequency = 131; uint8_t Left = 0, Right = 0; static const int i2s_num = 0; // i2s port number
// // Stereo tones. 4 levels (1 - 4) of logarithmic tones, plus tone OFF (0) per channel. // void Tones (uint32_t frequency, int8_t levelLeft, int8_t levelRight){
dac_cw_scale_t Scale;
switch (levelLeft) {
case 0:
dac_output_disable(DAC_CHANNEL_1); // off
break;
case 1:
dac_output_enable(DAC_CHANNEL_1);
Scale = DAC_CW_SCALE_8; // quietest
break;
case 2:
dac_output_enable(DAC_CHANNEL_1);
Scale = DAC_CW_SCALE_4;
break;
case 3:
dac_output_enable(DAC_CHANNEL_1);
Scale = DAC_CW_SCALE_2;
break;
case 4:
dac_output_enable(DAC_CHANNEL_1);
Scale = DAC_CW_SCALE_1; // loudest
break;
default:
dac_output_disable(DAC_CHANNEL_1);
break;
}
dac_cw_config = {
.en_ch = DAC_CHANNEL_1,
.scale = Scale,
.phase = DAC_CW_PHASE_0,
.freq = frequency,
.offset = 127
};
dac_cw_generator_config (&dac_cw_config);
switch (levelRight) {
case 0:
dac_output_disable(DAC_CHANNEL_2); // off
break;
case 1:
dac_output_enable(DAC_CHANNEL_2);
Scale = DAC_CW_SCALE_8; // quietest
break;
case 2:
dac_output_enable(DAC_CHANNEL_2);
Scale = DAC_CW_SCALE_4;
break;
case 3:
dac_output_enable(DAC_CHANNEL_2);
Scale = DAC_CW_SCALE_2;
break;
case 4:
dac_output_enable(DAC_CHANNEL_2);
Scale = DAC_CW_SCALE_1; // loudest
break;
default:
dac_output_disable(DAC_CHANNEL_2);
break;
}
dac_cw_config = {
.en_ch = DAC_CHANNEL_2,
.scale = Scale,
.phase = DAC_CW_PHASE_0,
.freq = frequency,
.offset = 127
};
dac_cw_generator_config (&dac_cw_config);
};
void setup() { dac_output_enable(DAC_CHANNEL_1); dac_output_enable(DAC_CHANNEL_2); dacWrite(25, 127); // set zero point dacWrite(26, 127); // set zero point dac_cw_generator_enable(); Serial.begin (115200); delay (100); }
void loop() delay (62);
Tones (frequency, leftright, !leftright);
frequency += 20;
if (frequency > 1500){
frequency = 131;
leftright = !leftright;
delay (500);
}
}
Hello HuVVer, Thanks alot. I shall be testing this later today. I know there is an example in the ESPhome dpcs on how to add Arduino style extensions so I will be studying that first. Thats the first hurdle for me as I normally only use the IDF command line, although I have tried a few arduino style ESP32 projects in the past to see how it works. So I understanmd the code you have written.
Just as a matter of interest, when I add this code through an ESPhome YAML script and compile how does this affect Platform IO dependancies for the DAC interface, Is platform IO just included in the ESPHome toolchain and works more or less transparently ?
Thanks alot for your help.
HI WuVVer, I have now learnt to compile ESPhome with the command line and it seems to be a sort of command line arduino package. I have added a customer (empty device successfully) now trying to add your code. But I get errors on missing type assignments shown below.
These types are not in the dac.h file and I dont see them in the orioginal example. I wonder if I am missing a layer that you added, where these types that you use in the interfcae calls are defined. ??
regards
Nick
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV Compiling .pioenvs\ttgotest1\src\main.cpp.o In file included from src\main.cpp:28:0: src\my_custom_sensor.h:7:1: error: 'dac_cw_config_t' does not name a type dac_cw_config_t dac_cw_config; ^ src\my_custom_sensor.h: In member function 'void MyCustomSensor::Tones(uint32_t, int8_t, int8_t)': src\my_custom_sensor.h:19:2: error: 'dac_cw_scale_t' was not declared in this scope dac_cw_scale_t Scale; ^ src\my_custom_sensor.h:21:48: error: 'Scale' was not declared in this scope case 1: dac_output_enable(DAC_CHANNEL_1); Scale = DAC_CW_SCALE_8; // quietest break; ^ src\my_custom_sensor.h:21:56: error: 'DAC_CW_SCALE_8' was not declared in this scope case 1: dac_output_enable(DAC_CHANNEL_1); Scale = DAC_CW_SCALE_8; // quietest break;
Hi, well I solved my problem. I went back to the original example and checked some of the incloude files in hal_dac.c and moved the C code into a emprty ESPhome custome component, and he prestot ot worked. This is my ESPhome yamal file:
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv esphome: includes:
my_custom_sensor.h
name: ttgotest1
esp32: board: esp32dev framework: type: arduino
logger:
api: password: ""
ota: password: ""
wifi: ssid: "work2" password: "tangray21"
ap: ssid: "Ttgotest1 Fallback Hotspot" password: "nZfEshRRytzU"
captive_portal:
light:
sensor:
platform: custom lambda: |- auto my_sensor = new MyCustomSensor(); App.register_component(my_sensor); return {my_sensor};
sensors: name: "My Custom Sensor"^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
And this is my C/C++ test case.
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
int clk_8m_div = 0; // RTC 8M clock divider (division is by clk_8m_div+1, i.e. 0 means 8MHz frequency) int frequency_step = 8; // Frequency step for CW generator int scale = 1; // 50% of the full scale int offset; // leave it default / 0 = no any offset int invert = 2; // invert MSB to get sine waveform char sbuf[80];
class MyCustomSensor : public PollingComponent, public Sensor {
void dac_cosine_enable(dac_channel_t channel) { // Enable tone generator common to both channels SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_SW_TONE_EN); switch(channel) { case DAC_CHANNEL_1: // Enable / connect tone tone generator on / to this channel SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN1_M); // Invert MSB, otherwise part of waveform will have inverted SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV1, 2, SENS_DAC_INV1_S); break; case DAC_CHANNEL_2: SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN2_M); SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV2, 2, SENS_DAC_INV2_S); break; default : printf("Channel %d\n", channel); } }
void dac_frequency_set(int clk_8m_div, int frequency_step) { REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL, clk_8m_div); SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL1_REG, SENS_SW_FSTEP, frequency_step, SENS_SW_FSTEP_S); }
/*
*/
void dac_scale_set(dac_channel_t channel, int scale)
{
switch(channel) {
case DAC_CHANNEL_1:
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_SCALE1, scale, SENS_DAC_SCALE1_S);
break;
case DAC_CHANNEL_2:
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_SCALE2, scale, SENS_DAC_SCALE2_S);
break;
default :
sprintf(sbuf,"Channel %d\n", channel);
ESP_LOGD("TEST: ",sbuf);
}
}
/*
*/
void dac_offset_set(dac_channel_t channel, int offset)
{
switch(channel) {
case DAC_CHANNEL_1:
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_DC1, offset, SENS_DAC_DC1_S);
break;
case DAC_CHANNEL_2:
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_DC2, offset, SENS_DAC_DC2_S);
break;
default :
sprintf(sbuf,"Channel %d\n", channel);
ESP_LOGD("TEST: ",sbuf);
}
}
/*
*/
void dac_invert_set(dac_channel_t channel, int invert)
{
switch(channel) {
case DAC_CHANNEL_1:
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV1, invert, SENS_DAC_INV1_S);
break;
case DAC_CHANNEL_2:
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV2, invert, SENS_DAC_INV2_S);
break;
default :
sprintf(sbuf,"Channel %d\n", channel);
ESP_LOGD("TEST: ",sbuf);
}
}
public: float count = 10.0;
// constructor MyCustomSensor() : PollingComponent(5000) {}
float get_setup_priority() const override { return esphome::setup_priority::HARDWARE; }
void setup() override { // This will be called by App.setup() dac_cosine_enable(DAC_CHANNEL_1); dac_cosine_enable(DAC_CHANNEL_2);
dac_output_enable(DAC_CHANNEL_1);
dac_output_enable(DAC_CHANNEL_2);
}
void update() override { // This will be called every "update_interval" milliseconds. publish_state(count); count = count +1; if (count == 40) count = 10;
// frequency setting is common to both channels dac_frequency_set(clk_8m_div, frequency_step); /* Tune parameters of channel 2 only
to see and compare changes against channel 1 */ dac_scale_set(DAC_CHANNEL_2, scale); dac_offset_set(DAC_CHANNEL_2, offset); dac_invert_set(DAC_CHANNEL_2, invert);
scale++; if (scale == 4) scale = 0;
float frequency = RTC_FAST_CLK_FREQ_APPROX / (1 + clk_8m_div) * (float) frequency_step / 65536;
sprintf(sbuf,"clk_8m_div: %d, frequency step: %d, frequency: %.0f Hz\n", clk_8m_div, frequency_step, frequency);
ESP_LOGD("TEST: ",sbuf);
sprintf(sbuf,"DAC2 scale: %d, offset %d, invert: %d\n", scale, offset, invert);
ESP_LOGD("TEST: ",sbuf);
}
};
So I get this feature request issue is closed.
Describe the problem you have/What new integration you would like
ESP32 has a built in cosine generator see https://github.com/krzychb/dac-cosine I would like to add this to toESPHome and use it like ledC pwm I have the relevant ESP32 and IDF experience but have not compiled ESPHome from scratch. Is there anyone who has added the cosine generator already or is there anyone that would like to help me do this. Please describe your use case for this integration and alternatives you've tried:
I need to use a sine wave (10 -50KHz) to for generating AC signals, neasuring response and applying ouput to opamp gain circuits. I would use this in a very similar way to using the ESP32 ledC output. API. Entity state is on/off and attributes would be chan nr , frequency and possibly amplitude. I should add that using the cosine generator adds no CPU overhead whereas an interrupt driven dac loads the cpu and may affect other running timers. So there is a clear advantage in going this route. Additional context
I have 5 years low level ESP32 experience with the ESP IDF and would like to compile ESPhome in IDF mode but have not got any experience with this and would like some help in setting up the correct tool chain