diff --git a/README.md b/README.md index 2eb70b1..b372f2a 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,24 @@ mruby-esp32-gpio GPIO library for mruby-esp32. ## Installation -Add the line below to your `build_config.rb`: +In `esp32_build_config.rb`, at the end of the `MRuby::CrossBuild` block, add the following: ```ruby conf.gem :github => 'mruby-esp32/mruby-esp32-gpio' ``` +In `CMakeLists.txt`, make sure the line `PRIV_REQUIRES` includes the `driver` component. Add it if needed: + +``` + add_prebuilt_library( + libmruby ${LIBMRUBY_FILE} + PRIV_REQUIRES esp_wifi esp_hw_support esp_rom mqtt driver + ) +```` + ## Example ```ruby +include ESP32::Constants include ESP32::GPIO led = GPIO_NUM_4 diff --git a/mrblib/gpio.rb b/mrblib/gpio.rb index 8b3cacc..7bf8916 100644 --- a/mrblib/gpio.rb +++ b/mrblib/gpio.rb @@ -1,23 +1,32 @@ module ESP32 + include Constants + module GPIO - include Constants + HIGH = ESP32::GPIO_HIGH + LOW = ESP32::GPIO_LOW - class << self - alias :digital_write :digitalWrite - alias :digital_read :digitalRead - alias :analog_write :analogWrite - alias :analog_read :analogRead - alias :pin_mode :pinMode - alias :hall_read :hallRead - end - + INPUT = ESP32::GPIO_MODE_INPUT + OUTPUT = ESP32::GPIO_MODE_OUTPUT + INPUT_PULLUP = ESP32::GPIO_MODE_INPUT_PULLUP + INPUT_PULLDOWN = ESP32::GPIO_MODE_INPUT_PULLDOWN + INPUT_PULLUP_PULLDOWN = ESP32::GPIO_MODE_INPUT_PULLUP_PULLDOWN + INPUT_OUTPUT = ESP32::GPIO_MODE_INPUT_OUTPUT + INPUT_OUTPUT_OD = ESP32::GPIO_MODE_INPUT_OUTPUT_OD + OUTPUT_OD = ESP32::GPIO_MODE_OUTPUT_OD + class Pin + HIGH = ESP32::GPIO_HIGH + LOW = ESP32::GPIO_LOW + PIN_MODE = { - pullup: ESP32::GPIO::INPUT_PULLUP, - pulldown: ESP32::GPIO::INPUT_PULLDOWN, - input: ESP32::GPIO::INPUT, - output: ESP32::GPIO::OUTPUT, - inout: ESP32::GPIO::INPUT_OUTPUT + input: ESP32::GPIO_MODE_INPUT, + output: ESP32::GPIO_MODE_OUTPUT, + input_pullup: ESP32::GPIO_MODE_INPUT_PULLUP, + input_pulldown: ESP32::GPIO_MODE_INPUT_PULLDOWN, + input_pullup_pulldown: ESP32::GPIO_MODE_INPUT_PULLUP_PULLDOWN, + input_output: ESP32::GPIO_MODE_INPUT_OUTPUT, + input_output_od: ESP32::GPIO_MODE_INPUT_OUTPUT_OD, + output_od: ESP32::GPIO_MODE_OUTPUT_OD, } attr_reader :pin diff --git a/src/gpio.c b/src/gpio.c index 6ad6e01..a969810 100644 --- a/src/gpio.c +++ b/src/gpio.c @@ -2,18 +2,27 @@ #include #include "driver/gpio.h" -#include "driver/dac.h" -#include "driver/adc.h" - -#define GPIO_MODE_DEF_PULLUP (BIT3) -#define GPIO_MODE_DEF_PULLDOWN (BIT3) -#define GPIO_MODE_INPUT_PULLUP ((GPIO_MODE_INPUT)|(GPIO_MODE_DEF_PULLUP)) -#define GPIO_MODE_INPUT_PULLDOWN ((GPIO_MODE_INPUT)|(GPIO_MODE_DEF_PULLDOWN)) -#define GPIO_MODE_OUTPUT (GPIO_MODE_DEF_OUTPUT) -#define GPIO_MODE_OUTPUT_OD ((GPIO_MODE_DEF_OUTPUT)|(GPIO_MODE_DEF_OD)) -#define GPIO_MODE_INPUT_OUTPUT_OD ((GPIO_MODE_DEF_INPUT)|(GPIO_MODE_DEF_OUTPUT)|(GPIO_MODE_DEF_OD)) -#define GPIO_MODE_INPUT_OUTPUT ((GPIO_MODE_DEF_INPUT)|(GPIO_MODE_DEF_OUTPUT)) - +#include "driver/dac_oneshot.h" +#include "esp_adc/adc_oneshot.h" + +// Defined by ESP-IDF: +// GPIO_MODE_DEF_INPUT (BIT0) +// GPIO_MODE_DEF_OUTPUT (BIT1) +// GPIO_MODE_DEF_OD (BIT2) +#define GPIO_MODE_DEF_PULLUP (BIT3) +#define GPIO_MODE_DEF_PULLDOWN (BIT4) + +// Defined by ESP-IDF: +// GPIO_MODE_INPUT +// GPIO_MODE_OUTPUT +#define GPIO_MODE_INPUT_PULLUP ((GPIO_MODE_DEF_INPUT)|(GPIO_MODE_DEF_PULLUP)) +#define GPIO_MODE_INPUT_PULLDOWN ((GPIO_MODE_DEF_INPUT)|(GPIO_MODE_DEF_PULLDOWN)) +#define GPIO_MODE_INPUT_PULLUP_PULLDOWN ((GPIO_MODE_DEF_INPUT)|(GPIO_MODE_DEF_PULLUP)|(GPIO_MODE_DEF_PULLDOWN)) +#define GPIO_MODE_INPUT_OUTPUT ((GPIO_MODE_DEF_INPUT) |(GPIO_MODE_DEF_OUTPUT)) +#define GPIO_MODE_INPUT_OUTPUT_OD ((GPIO_MODE_DEF_INPUT) |(GPIO_MODE_DEF_OUTPUT)|(GPIO_MODE_DEF_OD)) +#define GPIO_MODE_OUTPUT_OD ((GPIO_MODE_DEF_OUTPUT)|(GPIO_MODE_DEF_OD)) + +// Pin Mode static mrb_value mrb_esp32_gpio_pin_mode(mrb_state *mrb, mrb_value self) { mrb_value pin, dir; @@ -24,61 +33,54 @@ mrb_esp32_gpio_pin_mode(mrb_state *mrb, mrb_value self) { return mrb_nil_value(); } - gpio_pad_select_gpio(mrb_fixnum(pin)); - gpio_set_direction(mrb_fixnum(pin), mrb_fixnum(dir) & ~GPIO_MODE_DEF_PULLUP); - - if (mrb_fixnum(dir) & GPIO_MODE_DEF_PULLUP) { - gpio_set_pull_mode(mrb_fixnum(pin), GPIO_PULLUP_ONLY); + esp_rom_gpio_pad_select_gpio(mrb_fixnum(pin)); + + // Clear pullup and pulldown bits (BIT3 and BIT4) when setting direction. + gpio_set_direction(mrb_fixnum(pin), mrb_fixnum(dir) & ~(GPIO_MODE_DEF_PULLUP | GPIO_MODE_DEF_PULLDOWN)); + + // Set correct pull mode based on bits 3 and 4. + uint32_t pull = mrb_fixnum(dir) >> 3; + switch(pull) { + case 0: gpio_set_pull_mode(mrb_fixnum(pin), GPIO_FLOATING); break; + case 1: gpio_set_pull_mode(mrb_fixnum(pin), GPIO_PULLUP_ONLY); break; + case 2: gpio_set_pull_mode(mrb_fixnum(pin), GPIO_PULLDOWN_ONLY); break; + case 3: gpio_set_pull_mode(mrb_fixnum(pin), GPIO_PULLUP_PULLDOWN); break; } return self; } +// Digital Read static mrb_value -mrb_esp32_gpio_digital_write(mrb_state *mrb, mrb_value self) { - mrb_value pin, level; +mrb_esp32_gpio_digital_read(mrb_state *mrb, mrb_value self) { + mrb_value pin; - mrb_get_args(mrb, "oo", &pin, &level); + mrb_get_args(mrb, "o", &pin); - if (!mrb_fixnum_p(pin) || !mrb_fixnum_p(level)) { + if (!mrb_fixnum_p(pin)) { return mrb_nil_value(); } - gpio_set_level(mrb_fixnum(pin), mrb_fixnum(level)); - - return self; + return mrb_fixnum_value(gpio_get_level(mrb_fixnum(pin))); } +// Digital Write static mrb_value -mrb_esp32_gpio_analog_write(mrb_state *mrb, mrb_value self) { - mrb_value ch, vol; +mrb_esp32_gpio_digital_write(mrb_state *mrb, mrb_value self) { + mrb_value pin, level; - mrb_get_args(mrb, "oo", &ch, &vol); + mrb_get_args(mrb, "oo", &pin, &level); - if (!mrb_fixnum_p(ch) || !mrb_fixnum_p(vol)) { + if (!mrb_fixnum_p(pin) || !mrb_fixnum_p(level)) { return mrb_nil_value(); } - dac_output_enable(mrb_fixnum(ch)); - - dac_output_voltage(mrb_fixnum(ch), mrb_fixnum(vol)); + gpio_set_level(mrb_fixnum(pin), mrb_fixnum(level)); return self; } -static mrb_value -mrb_esp32_gpio_digital_read(mrb_state *mrb, mrb_value self) { - mrb_value pin; - - mrb_get_args(mrb, "o", &pin); - - if (!mrb_fixnum_p(pin)) { - return mrb_nil_value(); - } - - return mrb_fixnum_value(gpio_get_level(mrb_fixnum(pin))); -} - +// Analog Read static mrb_value mrb_esp32_gpio_analog_read(mrb_state *mrb, mrb_value self) { mrb_value ch; @@ -88,41 +90,105 @@ mrb_esp32_gpio_analog_read(mrb_state *mrb, mrb_value self) { if (!mrb_fixnum_p(ch)) { return mrb_nil_value(); } - - adc1_config_channel_atten(mrb_fixnum(ch), ADC_ATTEN_11db); - - return mrb_fixnum_value(adc1_get_raw(mrb_fixnum(ch))); + + // Handle + adc_oneshot_unit_handle_t adc1_handle; + adc_oneshot_unit_init_cfg_t init_config1 = { + .unit_id = ADC_UNIT_1, + .ulp_mode = ADC_ULP_MODE_DISABLE, + }; + ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle)); + + // Always use maximum resolution and attenuation. + // Should make this configurable. + adc_oneshot_chan_cfg_t config = { + .bitwidth = ADC_BITWIDTH_DEFAULT, + .atten = ADC_ATTEN_DB_11, + }; + ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, mrb_fixnum(ch), &config)); + + // Read and Delete + int adc_result; + ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, mrb_fixnum(ch), &adc_result)); + ESP_ERROR_CHECK(adc_oneshot_del_unit(adc1_handle)); + + return mrb_fixnum_value(adc_result); } +// Analog Write (DACs not available on some chips) +#ifdef SOC_DAC_SUPPORTED static mrb_value -mrb_esp32_gpio_hall_read(mrb_state *mrb, mrb_value self) { - return mrb_fixnum_value(hall_sensor_read()); +mrb_esp32_gpio_analog_write(mrb_state *mrb, mrb_value self) { + mrb_value ch, vol; + + mrb_get_args(mrb, "oo", &ch, &vol); + + if (!mrb_fixnum_p(ch) || !mrb_fixnum_p(vol)) { + return mrb_nil_value(); + } + + // Handle + dac_oneshot_handle_t chan_handle; + + // Configuration + dac_oneshot_config_t chan_cfg = { + .chan_id = mrb_fixnum(ch), + }; + ESP_ERROR_CHECK(dac_oneshot_new_channel(&chan_cfg, &chan_handle)); + + // Write + ESP_ERROR_CHECK(dac_oneshot_output_voltage(chan_handle, mrb_fixnum(vol))); + + return self; } +#endif void -mrb_mruby_esp32_gpio_gem_init(mrb_state* mrb) -{ +mrb_mruby_esp32_gpio_gem_init(mrb_state* mrb) { struct RClass *esp32, *gpio, *constants; + // ESP32 module esp32 = mrb_define_module(mrb, "ESP32"); + // ESP32::GPIO gpio = mrb_define_module_under(mrb, esp32, "GPIO"); + + // ESP32::Constants + constants = mrb_define_module_under(mrb, esp32, "Constants"); + + // Ruby-style snake-case methods. + mrb_define_module_function(mrb, gpio, "pin_mode", mrb_esp32_gpio_pin_mode, MRB_ARGS_REQ(2)); + mrb_define_module_function(mrb, gpio, "digital_write", mrb_esp32_gpio_digital_write, MRB_ARGS_REQ(2)); + mrb_define_module_function(mrb, gpio, "digital_read", mrb_esp32_gpio_digital_read, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, gpio, "analog_read", mrb_esp32_gpio_analog_read, MRB_ARGS_REQ(1)); + + // Arduino-style camel-case methods. mrb_define_module_function(mrb, gpio, "pinMode", mrb_esp32_gpio_pin_mode, MRB_ARGS_REQ(2)); mrb_define_module_function(mrb, gpio, "digitalWrite", mrb_esp32_gpio_digital_write, MRB_ARGS_REQ(2)); mrb_define_module_function(mrb, gpio, "digitalRead", mrb_esp32_gpio_digital_read, MRB_ARGS_REQ(1)); - mrb_define_module_function(mrb, gpio, "analogWrite", mrb_esp32_gpio_analog_write, MRB_ARGS_REQ(2)); mrb_define_module_function(mrb, gpio, "analogRead", mrb_esp32_gpio_analog_read, MRB_ARGS_REQ(1)); - mrb_define_module_function(mrb, gpio, "hallRead", mrb_esp32_gpio_hall_read, MRB_ARGS_NONE()); - - adc1_config_width(ADC_WIDTH_12Bit); - - constants = mrb_define_module_under(mrb, gpio, "Constants"); - -#define define_const(SYM) \ + + // DAC available only on some chips. + #ifdef SOC_DAC_SUPPORTED + mrb_define_const(mrb, constants, "SOC_DAC_SUPPORTED", mrb_true_value()); + mrb_define_module_function(mrb, gpio, "analogWrite", mrb_esp32_gpio_analog_write, MRB_ARGS_REQ(2)); + mrb_define_module_function(mrb, gpio, "analog_write", mrb_esp32_gpio_analog_write, MRB_ARGS_REQ(2)); + #else + mrb_define_const(mrb, constants, "SOC_DAC_SUPPORTED", mrb_false_value()); + #endif + + // Pass a C constant through to mruby, defined inside ESP32::Constants. + #define define_const(SYM) \ do { \ mrb_define_const(mrb, constants, #SYM, mrb_fixnum_value(SYM)); \ } while (0) + // + // GPIO numbers available on each variant found here: + // https://github.com/espressif/esp-idf/blob/67552c31da/components/hal/include/hal/gpio_types.h + // + // All chips define GPIO_NUM_MAX and GPIO_NUM_0..GPIO_NUM_20. + define_const(GPIO_NUM_MAX); define_const(GPIO_NUM_0); define_const(GPIO_NUM_1); define_const(GPIO_NUM_2); @@ -143,48 +209,122 @@ mrb_mruby_esp32_gpio_gem_init(mrb_state* mrb) define_const(GPIO_NUM_17); define_const(GPIO_NUM_18); define_const(GPIO_NUM_19); - - define_const(GPIO_NUM_21); - define_const(GPIO_NUM_22); - define_const(GPIO_NUM_23); - - define_const(GPIO_NUM_25); - define_const(GPIO_NUM_26); - define_const(GPIO_NUM_27); - - define_const(GPIO_NUM_32); - define_const(GPIO_NUM_33); - define_const(GPIO_NUM_34); - define_const(GPIO_NUM_35); - define_const(GPIO_NUM_36); - define_const(GPIO_NUM_37); - define_const(GPIO_NUM_38); - define_const(GPIO_NUM_39); - define_const(GPIO_NUM_MAX); - - define_const(DAC_CHANNEL_1); - define_const(DAC_CHANNEL_2); - define_const(DAC_CHANNEL_MAX); - - define_const(ADC1_CHANNEL_0); - define_const(ADC1_CHANNEL_1); - define_const(ADC1_CHANNEL_2); - define_const(ADC1_CHANNEL_3); - define_const(ADC1_CHANNEL_4); - define_const(ADC1_CHANNEL_5); - define_const(ADC1_CHANNEL_6); - define_const(ADC1_CHANNEL_7); - define_const(ADC1_CHANNEL_MAX); - - mrb_define_const(mrb, constants, "LOW", mrb_fixnum_value(0)); - mrb_define_const(mrb, constants, "HIGH", mrb_fixnum_value(1)); - - mrb_define_const(mrb, constants, "INPUT", mrb_fixnum_value(GPIO_MODE_INPUT)); - mrb_define_const(mrb, constants, "INPUT_OUTPUT", mrb_fixnum_value(GPIO_MODE_INPUT_OUTPUT)); - mrb_define_const(mrb, constants, "OUTPUT", mrb_fixnum_value(GPIO_MODE_OUTPUT)); - mrb_define_const(mrb, constants, "INPUT_PULLUP", mrb_fixnum_value(GPIO_MODE_INPUT_PULLUP)); - mrb_define_const(mrb, constants, "INPUT_PULLDOWN", mrb_fixnum_value(GPIO_MODE_INPUT_PULLDOWN)); + define_const(GPIO_NUM_20); + + // Original, S2, S3, C3, C6 and H2 (all except C2) have 21. + #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || \ + defined(CONFIG_IDF_TARGET_ESP32S3)|| defined(CONFIG_IDF_TARGET_ESP32C3) || \ + defined(CONFIG_IDF_TARGET_ESP32C6)|| defined(CONFIG_IDF_TARGET_ESP32H2) + define_const(GPIO_NUM_21); + #endif + + // Original, C6 and H2 have 22,23,25. + #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32C6) || \ + defined(CONFIG_IDF_TARGET_ESP32H2) + define_const(GPIO_NUM_22); + define_const(GPIO_NUM_23); + define_const(GPIO_NUM_25); + #endif + + // C6 and H2 have 24. + #if defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) + define_const(GPIO_NUM_24); + #endif + + // Original, S2, S3, C6 ad H2 have 26,27. + #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || \ + defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6) || \ + defined(CONFIG_IDF_TARGET_ESP32H2) + define_const(GPIO_NUM_26); + define_const(GPIO_NUM_27); + #endif + + // Original, S2, S3 and C6 have 28..30. + #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || \ + defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6) + define_const(GPIO_NUM_28); + define_const(GPIO_NUM_29); + define_const(GPIO_NUM_30); + #endif + // Original, S2 and S3 have 31..39. + #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || \ + defined(CONFIG_IDF_TARGET_ESP32S3) + define_const(GPIO_NUM_31); + define_const(GPIO_NUM_32); + define_const(GPIO_NUM_33); + define_const(GPIO_NUM_34); + define_const(GPIO_NUM_35); + define_const(GPIO_NUM_36); + define_const(GPIO_NUM_37); + define_const(GPIO_NUM_38); + define_const(GPIO_NUM_39); + #endif + + // S2 and S3 have 40..46. + #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) + define_const(GPIO_NUM_40); + define_const(GPIO_NUM_41); + define_const(GPIO_NUM_42); + define_const(GPIO_NUM_43); + define_const(GPIO_NUM_44); + define_const(GPIO_NUM_45); + define_const(GPIO_NUM_46); + #endif + + // S3 alone has 47,48. + #if defined(CONFIG_IDF_TARGET_ESP32S3) + define_const(GPIO_NUM_47); + define_const(GPIO_NUM_48); + #endif + + // + // All chips have ADC_CHANNEL_0..ADC_CHANNEL_9 defined, but limit them instead + // to the channels which are actually connected to GPIOs. + // + // All chips connect ADC_CHANNEL_0..ADC_CHANNEL_4 to a GPIO. + define_const(ADC_CHANNEL_0); + define_const(ADC_CHANNEL_1); + define_const(ADC_CHANNEL_2); + define_const(ADC_CHANNEL_3); + define_const(ADC_CHANNEL_4); + + // Original, S2, S3 and C6 have 5,6. + #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || \ + defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6) + define_const(ADC_CHANNEL_5); + define_const(ADC_CHANNEL_6); + #endif + + // Original, S2 and S3 have 7,8,9. + // Note: Original ESP32 has 8,9 only on ADC2 which isn't implemented yet. + #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || \ + defined(CONFIG_IDF_TARGET_ESP32S3) + define_const(ADC_CHANNEL_7); + define_const(ADC_CHANNEL_8); + define_const(ADC_CHANNEL_9); + #endif + + // Original and S2 have DACs. + #ifdef SOC_DAC_SUPPORTED + define_const(DAC_CHAN_0); + define_const(DAC_CHAN_1); + // Old versions of above. Deprecated. + define_const(DAC_CHANNEL_1); + define_const(DAC_CHANNEL_2); + #endif + + mrb_define_const(mrb, constants, "GPIO_LOW", mrb_fixnum_value(0)); + mrb_define_const(mrb, constants, "GPIO_HIGH", mrb_fixnum_value(1)); + + define_const(GPIO_MODE_INPUT); + define_const(GPIO_MODE_OUTPUT); + define_const(GPIO_MODE_INPUT_PULLUP); + define_const(GPIO_MODE_INPUT_PULLDOWN); + define_const(GPIO_MODE_INPUT_PULLUP_PULLDOWN); + define_const(GPIO_MODE_INPUT_OUTPUT); + define_const(GPIO_MODE_INPUT_OUTPUT_OD); + define_const(GPIO_MODE_OUTPUT_OD); } void