/* * MIT License * * Copyright (c) 2017 David Antliff * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_system.h" #include "esp_log.h" #include "driver/gpio.h" #include "driver/ledc.h" #include "owb.h" #include "owb_rmt.h" #include "ds18b20.h" #define GPIO_DS18B20_0 (CONFIG_ONE_WIRE_GPIO) #define MAX_DEVICES (8) #define DS18B20_RESOLUTION (DS18B20_RESOLUTION_12_BIT) #define SAMPLE_PERIOD (1000) // milliseconds static void led_task(void *pvParameters) { // Prepare and then apply the LEDC PWM timer configuration ledc_timer_config_t ledc_timer_0 = { .speed_mode = LEDC_HIGH_SPEED_MODE, .timer_num = LEDC_TIMER_0, .duty_resolution = LEDC_TIMER_8_BIT, .freq_hz = 1000, // Set output frequency at 5 kHz .clk_cfg = LEDC_AUTO_CLK }; ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer_0)); // Prepare and then apply the LEDC PWM channel configuration ledc_channel_config_t ledc_channel_1 = { .speed_mode = LEDC_HIGH_SPEED_MODE, .channel = LEDC_CHANNEL_0, .timer_sel = LEDC_TIMER_0, .intr_type = LEDC_INTR_DISABLE, .gpio_num = GPIO_NUM_19, .duty = 0, // Set duty to 0% .hpoint = 0 }; ledc_channel_config_t ledc_channel_2 = { .speed_mode = LEDC_HIGH_SPEED_MODE, .channel = LEDC_CHANNEL_1, .timer_sel = LEDC_TIMER_0, .intr_type = LEDC_INTR_DISABLE, .gpio_num = GPIO_NUM_21, .duty = 0, // Set duty to 0% .hpoint = 0 }; ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel_1)); ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel_2)); int steps[] = { 0, 16, 32, 48, 64, 80, 96, 112, 128, 128, 112, 96, 80, 64, 48, 32, 16, 0 }; int n_steps = sizeof(steps) / sizeof(steps[0]); for (;;) { for (int i = 0; i < n_steps; i++) { // Wait 10s and update the duty cycle vTaskDelay(100.0 / portTICK_PERIOD_MS); ESP_ERROR_CHECK(ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, steps[i])); ESP_ERROR_CHECK(ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1, steps[(i+4)%n_steps])); ESP_ERROR_CHECK(ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0)); ESP_ERROR_CHECK(ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1)); } } vTaskDelete(NULL); } _Noreturn void app_main() { // Override global log level esp_log_level_set("*", ESP_LOG_INFO); // To debug, use 'make menuconfig' to set default Log level to DEBUG, then uncomment: //esp_log_level_set("owb", ESP_LOG_DEBUG); //esp_log_level_set("ds18b20", ESP_LOG_DEBUG); BaseType_t xReturned; xReturned = xTaskCreate(led_task, "led_task", 4096, /* Stack size in words, not bytes. */ NULL, /* Parameter passed into the task. */ tskIDLE_PRIORITY + 12, NULL); if (xReturned != pdPASS) { ESP_LOGE("led", "xTaskCreate('led_task'): %d", xReturned); abort(); } // Stable readings require a brief period before communication vTaskDelay(2000.0 / portTICK_PERIOD_MS); // Create a 1-Wire bus, using the RMT timeslot driver OneWireBus * owb; owb_rmt_driver_info rmt_driver_info; owb = owb_rmt_initialize(&rmt_driver_info, GPIO_DS18B20_0, RMT_CHANNEL_1, RMT_CHANNEL_0); owb_use_crc(owb, true); // enable CRC check for ROM code // Find all connected devices printf("Find devices:\n"); OneWireBus_ROMCode device_rom_codes[MAX_DEVICES] = {0}; int num_devices = 0; OneWireBus_SearchState search_state = {0}; bool found = false; owb_search_first(owb, &search_state, &found); while (found) { char rom_code_s[17]; owb_string_from_rom_code(search_state.rom_code, rom_code_s, sizeof(rom_code_s)); printf(" %d : %s\n", num_devices, rom_code_s); device_rom_codes[num_devices] = search_state.rom_code; ++num_devices; owb_search_next(owb, &search_state, &found); } printf("Found %d device%s\n", num_devices, num_devices == 1 ? "" : "s"); // Create DS18B20 devices on the 1-Wire bus DS18B20_Info * devices[MAX_DEVICES] = {0}; for (int i = 0; i < num_devices; ++i) { DS18B20_Info * ds18b20_info = ds18b20_malloc(); // heap allocation devices[i] = ds18b20_info; if (num_devices == 1) { printf("Single device optimisations enabled\n"); ds18b20_init_solo(ds18b20_info, owb); // only one device on bus } else { ds18b20_init(ds18b20_info, owb, device_rom_codes[i]); // associate with bus and device } ds18b20_use_crc(ds18b20_info, true); // enable CRC check on all reads ds18b20_set_resolution(ds18b20_info, DS18B20_RESOLUTION); } // Check for parasitic-powered devices bool parasitic_power = false; ds18b20_check_for_parasite_power(owb, ¶sitic_power); if (parasitic_power) { printf("Parasitic-powered devices detected"); } // In parasitic-power mode, devices cannot indicate when conversions are complete, // so waiting for a temperature conversion must be done by waiting a prescribed duration owb_use_parasitic_power(owb, parasitic_power); #ifdef CONFIG_ENABLE_STRONG_PULLUP_GPIO // An external pull-up circuit is used to supply extra current to OneWireBus devices // during temperature conversions. owb_use_strong_pullup_gpio(owb, CONFIG_STRONG_PULLUP_GPIO); #endif // Read temperatures more efficiently by starting conversions on all devices at the same time int errors_count[MAX_DEVICES] = {0}; int sample_count = 0; if (num_devices > 0) { TickType_t last_wake_time = xTaskGetTickCount(); while (1) { ds18b20_convert_all(owb); // In this application all devices use the same resolution, // so use the first device to determine the delay ds18b20_wait_for_conversion(devices[0]); // Read the results immediately after conversion otherwise it may fail // (using printf before reading may take too long) float readings[MAX_DEVICES] = { 0 }; DS18B20_ERROR errors[MAX_DEVICES] = { 0 }; for (int i = 0; i < num_devices; ++i) { errors[i] = ds18b20_read_temp(devices[i], &readings[i]); } // Print results in a separate loop, after all have been read printf("\nTemperature readings (degrees C): sample %d\n", ++sample_count); for (int i = 0; i < num_devices; ++i) { if (errors[i] != DS18B20_OK) { ++errors_count[i]; } printf(" %d: %.1f %d errors\n", i, readings[i], errors_count[i]); } vTaskDelayUntil(&last_wake_time, SAMPLE_PERIOD / portTICK_PERIOD_MS); } } else { printf("\nNo DS18B20 devices detected!\n"); } // clean up dynamically allocated data for (int i = 0; i < num_devices; ++i) { ds18b20_free(&devices[i]); } owb_uninitialize(owb); printf("Restarting now.\n"); fflush(stdout); vTaskDelay(1000 / portTICK_PERIOD_MS); esp_restart(); }