denko 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build_atmega_avr.yml +2 -1
  3. data/.github/workflows/build_atmega_megaavr.yml +2 -1
  4. data/.github/workflows/build_atsam3x.yml +1 -0
  5. data/.github/workflows/build_atsamd21.yml +2 -1
  6. data/.github/workflows/build_esp32.yml +4 -2
  7. data/.github/workflows/build_esp32c3.yml +4 -3
  8. data/.github/workflows/build_esp32c6.yml +4 -2
  9. data/.github/workflows/build_esp32h2.yml +4 -2
  10. data/.github/workflows/build_esp32s2.yml +4 -2
  11. data/.github/workflows/build_esp32s3.yml +4 -2
  12. data/.github/workflows/build_esp8266.yml +2 -1
  13. data/.github/workflows/build_ra4m1.yml +1 -0
  14. data/.github/workflows/build_rp2040.yml +4 -3
  15. data/.github/workflows/ruby.yml +1 -1
  16. data/CHANGELOG.md +203 -0
  17. data/DEPS_CLI.md +16 -16
  18. data/DEPS_IDE.md +31 -30
  19. data/MICROCONTROLLERS.md +103 -0
  20. data/PERIPHERALS.md +178 -0
  21. data/README.md +28 -21
  22. data/denko.gemspec +6 -1
  23. data/lib/denko/analog_io/ads1118.rb +5 -5
  24. data/lib/denko/analog_io/ads111x.rb +23 -19
  25. data/lib/denko/analog_io/joystick.rb +87 -0
  26. data/lib/denko/analog_io/potentiometer.rb +1 -5
  27. data/lib/denko/analog_io.rb +22 -8
  28. data/lib/denko/behaviors/bus_controller.rb +2 -1
  29. data/lib/denko/behaviors/bus_peripheral.rb +1 -1
  30. data/lib/denko/behaviors/callbacks.rb +18 -16
  31. data/lib/denko/behaviors/component.rb +0 -4
  32. data/lib/denko/behaviors/lifecycle.rb +1 -1
  33. data/lib/denko/behaviors/listener.rb +9 -3
  34. data/lib/denko/behaviors/multi_pin.rb +4 -6
  35. data/lib/denko/behaviors/poller.rb +11 -2
  36. data/lib/denko/behaviors/reader.rb +109 -21
  37. data/lib/denko/behaviors/single_pin.rb +2 -4
  38. data/lib/denko/behaviors/state.rb +18 -13
  39. data/lib/denko/behaviors/threaded.rb +19 -8
  40. data/lib/denko/behaviors.rb +36 -23
  41. data/lib/denko/board/eeprom.rb +1 -1
  42. data/lib/denko/board/i2c.rb +1 -1
  43. data/lib/denko/board/i2c_bit_bang.rb +9 -5
  44. data/lib/denko/board/map.rb +6 -2
  45. data/lib/denko/board/one_wire.rb +3 -3
  46. data/lib/denko/board/spi.rb +30 -30
  47. data/lib/denko/board/spi_bit_bang.rb +8 -11
  48. data/lib/denko/board.rb +6 -3
  49. data/lib/denko/connection/flow_control.rb +1 -1
  50. data/lib/denko/connection/serial.rb +5 -5
  51. data/lib/denko/digital_io/output.rb +12 -4
  52. data/lib/denko/digital_io/pcf8574.rb +114 -0
  53. data/lib/denko/digital_io/rotary_encoder.rb +10 -6
  54. data/lib/denko/digital_io.rb +24 -6
  55. data/lib/denko/display/canvas.rb +350 -157
  56. data/lib/denko/display/font/bmp_5x7.rb +142 -0
  57. data/lib/denko/display/font/bmp_6x8.rb +142 -0
  58. data/lib/denko/display/font/bmp_8x16.rb +141 -0
  59. data/lib/denko/display/font.rb +22 -0
  60. data/lib/denko/display/hd44780.rb +24 -20
  61. data/lib/denko/display/il0373.rb +186 -0
  62. data/lib/denko/display/mono_oled.rb +193 -0
  63. data/lib/denko/display/pcd8544.rb +154 -0
  64. data/lib/denko/display/pixel_common.rb +83 -0
  65. data/lib/denko/display/sh1106.rb +17 -21
  66. data/lib/denko/display/sh1107.rb +10 -0
  67. data/lib/denko/display/spi_common.rb +35 -0
  68. data/lib/denko/display/spi_epaper_common.rb +30 -0
  69. data/lib/denko/display/ssd1306.rb +6 -228
  70. data/lib/denko/display/ssd1680.rb +14 -0
  71. data/lib/denko/display/ssd1681.rb +8 -0
  72. data/lib/denko/display/ssd168x.rb +227 -0
  73. data/lib/denko/display/st7302.rb +207 -0
  74. data/lib/denko/display/st7565.rb +166 -0
  75. data/lib/denko/display.rb +40 -4
  76. data/lib/denko/eeprom/at24c.rb +67 -0
  77. data/lib/denko/eeprom/board.rb +69 -0
  78. data/lib/denko/eeprom.rb +15 -1
  79. data/lib/denko/helpers/engine_check.rb +13 -0
  80. data/lib/denko/{mutex_stub.rb → helpers/mutex_stub.rb} +6 -0
  81. data/lib/denko/helpers.rb +6 -0
  82. data/lib/denko/i2c/bit_bang.rb +1 -0
  83. data/lib/denko/i2c/bus_common.rb +9 -4
  84. data/lib/denko/i2c/peripheral.rb +5 -1
  85. data/lib/denko/i2c.rb +17 -4
  86. data/lib/denko/led/apa102.rb +1 -3
  87. data/lib/denko/led/base.rb +5 -0
  88. data/lib/denko/led/rgb.rb +16 -10
  89. data/lib/denko/led/seven_segment.rb +1 -1
  90. data/lib/denko/led.rb +17 -8
  91. data/lib/denko/motor/{stepper.rb → a3967.rb} +1 -1
  92. data/lib/denko/motor/servo.rb +16 -6
  93. data/lib/denko/motor.rb +16 -3
  94. data/lib/denko/one_wire/bus.rb +20 -16
  95. data/lib/denko/one_wire/bus_enumerator.rb +25 -14
  96. data/lib/denko/one_wire/helper.rb +4 -2
  97. data/lib/denko/one_wire.rb +18 -5
  98. data/lib/denko/pulse_io/buzzer.rb +2 -6
  99. data/lib/denko/pulse_io/ir_output.rb +1 -5
  100. data/lib/denko/pulse_io/pwm_output.rb +56 -31
  101. data/lib/denko/pulse_io.rb +17 -3
  102. data/lib/denko/rtc/ds3231.rb +4 -3
  103. data/lib/denko/rtc.rb +14 -1
  104. data/lib/denko/sensor/aht.rb +16 -20
  105. data/lib/denko/sensor/bme280.rb +23 -36
  106. data/lib/denko/sensor/bmp180.rb +8 -13
  107. data/lib/denko/sensor/dht.rb +17 -7
  108. data/lib/denko/sensor/ds18b20.rb +5 -4
  109. data/lib/denko/sensor/hdc1080.rb +174 -0
  110. data/lib/denko/sensor/htu21d.rb +17 -6
  111. data/lib/denko/sensor/htu31d.rb +6 -5
  112. data/lib/denko/sensor/jsnsr04t.rb +49 -0
  113. data/lib/denko/sensor/qmp6988.rb +14 -30
  114. data/lib/denko/sensor/rcwl9620.rb +1 -0
  115. data/lib/denko/sensor/sht3x.rb +6 -5
  116. data/lib/denko/sensor/sht4x.rb +125 -0
  117. data/lib/denko/sensor/vl53l0x.rb +58 -0
  118. data/lib/denko/sensor.rb +33 -15
  119. data/lib/denko/spi/base_register.rb +11 -7
  120. data/lib/denko/spi/bus_common.rb +12 -15
  121. data/lib/denko/spi/input_register.rb +6 -6
  122. data/lib/denko/spi/output_register.rb +13 -4
  123. data/lib/denko/spi/peripheral.rb +82 -84
  124. data/lib/denko/spi.rb +20 -10
  125. data/lib/denko/uart/bit_bang.rb +2 -27
  126. data/lib/denko/uart/common.rb +33 -0
  127. data/lib/denko/uart/hardware.rb +1 -26
  128. data/lib/denko/uart.rb +16 -2
  129. data/lib/denko/version.rb +1 -1
  130. data/lib/denko.rb +22 -25
  131. data/lib/denko_cli/targets.rb +7 -7
  132. data/lib/denko_cli/targets.txt +19 -20
  133. data/src/lib/Denko.cpp +26 -13
  134. data/src/lib/Denko.h +4 -4
  135. data/src/lib/DenkoDefines.h +7 -26
  136. data/src/lib/DenkoLEDArray.cpp +1 -8
  137. data/src/lib/DenkoSPI.cpp +31 -29
  138. data/src/lib/DenkoSPIBB.cpp +12 -14
  139. data/test/analog_io/input_test.rb +1 -1
  140. data/test/analog_io/potentiometer_test.rb +2 -2
  141. data/test/behaviors/bus_peripheral_test.rb +4 -4
  142. data/test/behaviors/callbacks_test.rb +20 -10
  143. data/test/behaviors/component_test.rb +18 -8
  144. data/test/board/board_test.rb +9 -9
  145. data/test/board/one_wire_test.rb +25 -14
  146. data/test/board/spi_test.rb +31 -15
  147. data/test/digital_io/input_test.rb +2 -2
  148. data/test/display/canvas_test.rb +306 -0
  149. data/test/display/hd44780_test.rb +34 -7
  150. data/test/eeprom/board_test.rb +45 -0
  151. data/test/helpers/mruby_minitest.rb +95 -0
  152. data/test/helpers/mruby_runner.rb +13 -0
  153. data/test/i2c/bus_test.rb +93 -30
  154. data/test/i2c/peripheral_test.rb +2 -2
  155. data/test/led/apa102_test.rb +24 -0
  156. data/test/led/rgb_test.rb +4 -4
  157. data/test/motor/{stepper_test.rb → a3967_test.rb} +2 -2
  158. data/test/one_wire/bus_enumerator_test.rb +1 -1
  159. data/test/one_wire/bus_test.rb +42 -35
  160. data/test/one_wire/peripheral_test.rb +5 -17
  161. data/test/pulse_io/ir_output_test.rb +5 -0
  162. data/test/pulse_io/pwm_output_test.rb +10 -10
  163. data/test/rtc/ds3231_test.rb +3 -2
  164. data/test/sensor/dht_test.rb +11 -11
  165. data/test/spi/bitbang_test.rb +27 -0
  166. data/test/spi/bus_test.rb +19 -29
  167. data/test/spi/input_register_test.rb +2 -2
  168. data/test/spi/{peripheral_multi_pin_test.rb → peripheral_test.rb} +25 -5
  169. data/test/test_helper.rb +44 -124
  170. data/vendor/board-maps/BoardMap.h +264 -0
  171. data/vendor/board-maps/yaml/ALFREDO_NOU3.yml +2 -0
  172. data/vendor/board-maps/yaml/ATD143_S3.yml +1 -0
  173. data/vendor/board-maps/yaml/BHARATPI_A7672S_4G.yml +14 -0
  174. data/vendor/board-maps/yaml/BHARATPI_LORA.yml +14 -0
  175. data/vendor/board-maps/yaml/BHARATPI_NODE_WIFI.yml +14 -0
  176. data/vendor/board-maps/yaml/BPI_LEAF_S3.yml +0 -1
  177. data/vendor/board-maps/yaml/CEZERIO_DEV_ESP32C6.yml +14 -0
  178. data/vendor/board-maps/yaml/CEZERIO_MINI_DEV_ESP32C6.yml +12 -0
  179. data/vendor/board-maps/yaml/CIRCUITART_ZERO_S3.yml +71 -0
  180. data/vendor/board-maps/yaml/CODECELLC3.yml +13 -0
  181. data/vendor/board-maps/yaml/CYOBOT_V2_ESP32S3.yml +7 -0
  182. data/vendor/board-maps/yaml/EDGES3D.yml +25 -0
  183. data/vendor/board-maps/yaml/ESP32C6_DEV.yml +3 -4
  184. data/vendor/board-maps/yaml/ESP32C6_THING_PLUS.yml +0 -1
  185. data/vendor/board-maps/yaml/ESP32H2_DEV.yml +0 -1
  186. data/vendor/board-maps/yaml/ESP32H2_DEVKIT_LIPO.yml +0 -1
  187. data/vendor/board-maps/yaml/ESP32P4_DEV.yml +35 -0
  188. data/vendor/board-maps/yaml/ESP32S2_DEV.yml +0 -1
  189. data/vendor/board-maps/yaml/ESP32S2_DEVKIT_LIPO.yml +0 -1
  190. data/vendor/board-maps/yaml/ESP32S2_DEVKIT_LIPO_USB.yml +0 -1
  191. data/vendor/board-maps/yaml/ESP32_2432S028R.yml +14 -0
  192. data/vendor/board-maps/yaml/FEATHERS3.yml +1 -1
  193. data/vendor/board-maps/yaml/FRI3D_2024_ESP32S3.yml +43 -0
  194. data/vendor/board-maps/yaml/GEEKBLE_ESP32C3.yml +0 -1
  195. data/vendor/board-maps/yaml/GEEKBLE_NANO_ESP32S3.yml +25 -0
  196. data/vendor/board-maps/yaml/HELTEC_VISION_MASTER_E290.yml +41 -0
  197. data/vendor/board-maps/yaml/HELTEC_VISION_MASTER_E_213.yml +41 -0
  198. data/vendor/board-maps/yaml/HELTEC_VISION_MASTER_T190.yml +41 -0
  199. data/vendor/board-maps/yaml/HUIDU_HD_WF2.yml +5 -0
  200. data/vendor/board-maps/yaml/HUIDU_HD_WF4.yml +1 -0
  201. data/vendor/board-maps/yaml/LILYGO_LORA_CC1101.yml +6 -0
  202. data/vendor/board-maps/yaml/LILYGO_LORA_LR1121.yml +6 -0
  203. data/vendor/board-maps/yaml/LILYGO_LORA_SI4432.yml +6 -0
  204. data/vendor/board-maps/yaml/LILYGO_LORA_SX1262.yml +6 -0
  205. data/vendor/board-maps/yaml/LILYGO_LORA_SX1280.yml +6 -0
  206. data/vendor/board-maps/yaml/LOLIN_C3_MINI.yml +0 -1
  207. data/vendor/board-maps/yaml/LOLIN_C3_PICO.yml +1 -2
  208. data/vendor/board-maps/yaml/LoPy.yml +0 -1
  209. data/vendor/board-maps/yaml/LoPy4.yml +0 -1
  210. data/vendor/board-maps/yaml/M5STACK_DINMETER.yml +8 -0
  211. data/vendor/board-maps/yaml/M5STACK_FIRE.yml +1 -1
  212. data/vendor/board-maps/yaml/OMGS3.yml +25 -0
  213. data/vendor/board-maps/yaml/PCBCUPID_GLYPHC3.yml +23 -0
  214. data/vendor/board-maps/yaml/PCBCUPID_GLYPHC6.yml +32 -0
  215. data/vendor/board-maps/yaml/PCBCUPID_GLYPHH2.yml +24 -0
  216. data/vendor/board-maps/yaml/PYCOM_GPY.yml +0 -1
  217. data/vendor/board-maps/yaml/SENSEBOX_MCU_ESP32S2.yml +1 -1
  218. data/vendor/board-maps/yaml/SPARKFUN_ESP32S3_THING_PLUS.yml +13 -0
  219. data/vendor/board-maps/yaml/SPARKLEMOTIONMINI_ESP32.yml +12 -0
  220. data/vendor/board-maps/yaml/SPARKLEMOTIONSTICK_ESP32.yml +11 -0
  221. data/vendor/board-maps/yaml/SPARKLEMOTION_ESP32.yml +12 -0
  222. data/vendor/board-maps/yaml/SQUIXL.yml +7 -0
  223. data/vendor/board-maps/yaml/THINGPULSE_EPULSE_FEATHER_C6.yml +0 -1
  224. data/vendor/board-maps/yaml/T_LORA_PAGER.yml +6 -0
  225. data/vendor/board-maps/yaml/T_WATCH_S3.yml +7 -0
  226. data/vendor/board-maps/yaml/T_WATCH_S3_ULTRA.yml +6 -0
  227. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_LCD_146.yml +41 -0
  228. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_LCD_147.yml +41 -0
  229. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_LCD_169.yml +38 -0
  230. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_LCD_185.yml +41 -0
  231. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_RELAY_6CH.yml +41 -0
  232. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_TOUCH_AMOLED_143.yml +7 -0
  233. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_TOUCH_AMOLED_164.yml +7 -0
  234. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_TOUCH_AMOLED_18.yml +38 -0
  235. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_TOUCH_AMOLED_191.yml +7 -0
  236. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_TOUCH_AMOLED_241.yml +7 -0
  237. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_TOUCH_LCD_146.yml +41 -0
  238. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_TOUCH_LCD_169.yml +38 -0
  239. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_TOUCH_LCD_185.yml +41 -0
  240. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_TOUCH_LCD_185_BOX.yml +41 -0
  241. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_TOUCH_LCD_21.yml +41 -0
  242. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_TOUCH_LCD_28.yml +41 -0
  243. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_TOUCH_LCD_4.yml +36 -0
  244. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_TOUCH_LCD_43.yml +38 -0
  245. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_TOUCH_LCD_43B.yml +38 -0
  246. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_TOUCH_LCD_5.yml +38 -0
  247. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_TOUCH_LCD_5B.yml +38 -0
  248. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_TOUCH_LCD_7.yml +38 -0
  249. data/vendor/board-maps/yaml/WAVESHARE_ESP32_S3_ZERO.yml +36 -0
  250. data/vendor/board-maps/yaml/WIPY3.yml +0 -1
  251. data/vendor/board-maps/yaml/WS_ESP32_S3_MATRIX.yml +38 -0
  252. data/vendor/board-maps/yaml/XIAO_ESP32S3_PLUS.yml +46 -0
  253. data/vendor/board-maps/yaml/YB_ESP32S3_AMP_V2.yml +28 -0
  254. data/vendor/board-maps/yaml/YB_ESP32S3_AMP_V3.yml +28 -0
  255. data/vendor/board-maps/yaml/YB_ESP32S3_ETH.yml +40 -0
  256. data/vendor/board-maps/yaml/mercury.yml +20 -0
  257. metadata +116 -101
  258. data/.vscode/settings.json +0 -5
  259. data/.vscode/tasks.json +0 -20
  260. data/HARDWARE.md +0 -263
  261. data/benchmarks/analog_listen.rb +0 -49
  262. data/benchmarks/digital_write.rb +0 -28
  263. data/benchmarks/i2c_ssd1306_refresh.rb +0 -91
  264. data/examples/advanced/m5_env3.rb +0 -46
  265. data/examples/advanced/rotary_encoder_mac_volume.rb +0 -53
  266. data/examples/advanced/ssd1306_time_temp_rh.rb +0 -43
  267. data/examples/analog_io/ads1100.rb +0 -48
  268. data/examples/analog_io/ads1115.rb +0 -57
  269. data/examples/analog_io/ads1118.rb +0 -65
  270. data/examples/analog_io/dac_loopback.rb +0 -34
  271. data/examples/analog_io/input.rb +0 -56
  272. data/examples/analog_io/input_smoothing.rb +0 -27
  273. data/examples/analog_io/potentiometer.rb +0 -31
  274. data/examples/connection/binary_echo.rb +0 -34
  275. data/examples/connection/tcp.rb +0 -19
  276. data/examples/digital_io/button.rb +0 -17
  277. data/examples/digital_io/relay.rb +0 -17
  278. data/examples/digital_io/rotary_encoder.rb +0 -36
  279. data/examples/display/hd44780.png +0 -0
  280. data/examples/display/hd44780.rb +0 -47
  281. data/examples/display/ssd1306.rb +0 -43
  282. data/examples/display/ssd1306_s2_pico.rb +0 -29
  283. data/examples/eeprom/built_in.rb +0 -32
  284. data/examples/i2c/search.rb +0 -39
  285. data/examples/led/apa102_bounce.rb +0 -32
  286. data/examples/led/apa102_fade.rb +0 -44
  287. data/examples/led/builtin_blink.rb +0 -14
  288. data/examples/led/builtin_fade.rb +0 -19
  289. data/examples/led/rgb_led.rb +0 -31
  290. data/examples/led/seven_segment_char_echo.rb +0 -17
  291. data/examples/led/ws2812_bounce.rb +0 -30
  292. data/examples/led/ws2812_builtin_blink.rb +0 -22
  293. data/examples/led/ws2812_fade.rb +0 -43
  294. data/examples/motor/l298.rb +0 -45
  295. data/examples/motor/servo.rb +0 -17
  296. data/examples/motor/stepper.png +0 -0
  297. data/examples/motor/stepper.rb +0 -43
  298. data/examples/one_wire/search.rb +0 -32
  299. data/examples/pulse_io/buzzer.rb +0 -35
  300. data/examples/pulse_io/ir_output.rb +0 -51
  301. data/examples/pulse_io/pwm_output.rb +0 -30
  302. data/examples/rtc/ds3231.rb +0 -31
  303. data/examples/sensor/aht10.rb +0 -17
  304. data/examples/sensor/aht20.rb +0 -17
  305. data/examples/sensor/bme280.rb +0 -38
  306. data/examples/sensor/bmp180.rb +0 -26
  307. data/examples/sensor/dht.rb +0 -29
  308. data/examples/sensor/ds18b20.rb +0 -57
  309. data/examples/sensor/generic_pir.rb +0 -27
  310. data/examples/sensor/hcsr04.rb +0 -17
  311. data/examples/sensor/htu21d.rb +0 -43
  312. data/examples/sensor/htu31d.rb +0 -33
  313. data/examples/sensor/neat_tph_readings.rb +0 -32
  314. data/examples/sensor/qmp6988.rb +0 -51
  315. data/examples/sensor/rcwl9620.rb +0 -15
  316. data/examples/sensor/sht3x.rb +0 -32
  317. data/examples/spi/bitbang_loopback.rb +0 -46
  318. data/examples/spi/input_register.rb +0 -40
  319. data/examples/spi/output_register.rb +0 -41
  320. data/examples/spi/ssd_through_registers.rb +0 -28
  321. data/examples/spi/two_registers.rb +0 -40
  322. data/examples/uart/bit_bang_demo.rb +0 -25
  323. data/examples/uart/board_passthrough.rb +0 -40
  324. data/examples/uart/hardware_loopback.rb +0 -16
  325. data/lib/denko/eeprom/built_in.rb +0 -72
  326. data/lib/denko/fonts.rb +0 -106
  327. data/test/eeprom/built_in_test.rb +0 -61
  328. data/test/spi/peripheral_single_pin_test.rb +0 -48
  329. data/tutorial/01-led/led.fzz +0 -0
  330. data/tutorial/01-led/led.pdf +0 -0
  331. data/tutorial/01-led/led.rb +0 -73
  332. data/tutorial/02-button/button.fzz +0 -0
  333. data/tutorial/02-button/button.pdf +0 -0
  334. data/tutorial/02-button/button.rb +0 -65
  335. data/tutorial/03-potentiometer/potentiometer.fzz +0 -0
  336. data/tutorial/03-potentiometer/potentiometer.pdf +0 -0
  337. data/tutorial/03-potentiometer/potentiometer.rb +0 -66
  338. data/tutorial/04-pwm_led/pwm_led.fzz +0 -0
  339. data/tutorial/04-pwm_led/pwm_led.pdf +0 -0
  340. data/tutorial/04-pwm_led/pwm_led.rb +0 -64
  341. data/tutorial/05-rgb_led/rgb_led.fzz +0 -0
  342. data/tutorial/05-rgb_led/rgb_led.pdf +0 -0
  343. data/tutorial/05-rgb_led/rgb_led.rb +0 -58
  344. data/tutorial/05-rgb_led/rgb_mapping.rb +0 -76
@@ -1,27 +0,0 @@
1
- #
2
- # Example using a PIR motion sensor. Tested with AS312 and HC-SR501 sensors.
3
- #
4
- # General notes:
5
- # - Both sensors have a few seconds "dead time" after "motion stop" (logical 0), where further
6
- # motion will not trigger "motion start" (logical 1).
7
- #
8
- # HC-SR501 notes:
9
- # - Needs some time to warm up and start working properly.
10
- # - Set the time potentiometer to its lowest value.
11
- # - Make sure retriggering is enabled. It might be default, but there's a jumper to solder too.
12
- #
13
- require 'bundler/setup'
14
- require 'denko'
15
-
16
- PIN = 8
17
-
18
- board = Denko::Board.new(Denko::Connection::Serial.new)
19
- sensor = Denko::Sensor::GenericPIR.new(board: board, pin: PIN)
20
-
21
- sensor.on_motion_start { print "Motion detected! \r" }
22
- sensor.on_motion_stop { print "No motion detected... \r" }
23
-
24
- # Read initial state.
25
- sensor.read
26
-
27
- sleep
@@ -1,17 +0,0 @@
1
- #
2
- # Example of reading an HC-SR04 ultrasonic sensor.
3
- #
4
- require 'bundler/setup'
5
- require 'denko'
6
-
7
- TRIGGER_PIN = 6
8
- ECHO_PIN = 7
9
-
10
- board = Denko::Board.new(Denko::Connection::Serial.new)
11
- hcsr04 = Denko::Sensor::HCSR04.new(board: board, pins: {trigger: TRIGGER_PIN, echo: ECHO_PIN})
12
-
13
- hcsr04.poll(0.50) do |distance|
14
- puts "Distance: #{distance} mm"
15
- end
16
-
17
- sleep
@@ -1,43 +0,0 @@
1
- #
2
- # HTU21D sensor over I2C, for temperature and humidity.
3
- #
4
- require 'bundler/setup'
5
- require 'denko'
6
- require_relative 'neat_tph_readings'
7
-
8
- board = Denko::Board.new(Denko::Connection::Serial.new)
9
- bus = Denko::I2C::Bus.new(board: board)
10
- htu21d = Denko::Sensor::HTU21D.new(bus: bus) # address: 0x40 default
11
-
12
- # Get and set heater state.
13
- htu21d.heater_on
14
- puts "Heater on: #{htu21d.heater_on?}"
15
- htu21d.heater_off
16
- puts "Heater off: #{htu21d.heater_off?}"
17
- puts
18
-
19
- # Back to default settings, except heater state.
20
- htu21d.reset
21
-
22
- # Only 4 resolution combinations are available, and need to be
23
- # set by giving a bitmask from the datasheet:
24
- # 0x00 = 14-bit temperature, 12-bit humidity
25
- # 0x01 = 12-bit temperature, 8-bit humidity (default)
26
- # 0x80 = 13-bit temperature, 10-bit humidity
27
- # 0x81 = 11-bit temperature, 11-bit humidity
28
- #
29
- htu21d.resolution = 0x81
30
- puts "Temperature resolution: #{htu21d.resolution[:temperature]} bits"
31
- puts "Humidity resolution: #{htu21d.resolution[:humidity]} bits"
32
- puts
33
-
34
- htu21d.read
35
- puts "Temperature unit helpers: #{htu21d.temperature} \xC2\xB0C | #{htu21d.temperature_f} \xC2\xB0F | #{htu21d.temperature_k} K"
36
- puts
37
-
38
- # Poll it and print readings.
39
- htu21d.poll(5) do |reading|
40
- print_tph_reading(reading)
41
- end
42
-
43
- sleep
@@ -1,33 +0,0 @@
1
- #
2
- # HTU31D sensor over I2C, for temperature and humidity.
3
- #
4
- require 'bundler/setup'
5
- require 'denko'
6
- require_relative 'neat_tph_readings'
7
-
8
- board = Denko::Board.new(Denko::Connection::Serial.new)
9
- bus = Denko::I2C::Bus.new(board: board)
10
- sensor = Denko::Sensor::HTU31D.new(bus: bus) # address: 0x40 default
11
-
12
- # Get and set heater state.
13
- sensor.heater_on
14
- puts "Heater on: #{sensor.heater_on?}"
15
- sensor.heater_off
16
- puts "Heater off: #{sensor.heater_off?}"
17
-
18
- # Back to default settings, including heater off, unlike HTU21D.
19
- sensor.reset
20
- puts "Resetting HTU31D..."
21
- puts "Heater off: #{sensor.heater_off?}"
22
- puts
23
-
24
- # Resolution goes from 0..3 separately for temperature and humidity. See datasheet.
25
- sensor.temperature_resolution = 3
26
- sensor.humidity_resolution = 3
27
-
28
- # Poll it and print readings.
29
- sensor.poll(5) do |reading|
30
- print_tph_reading(reading)
31
- end
32
-
33
- sleep
@@ -1,32 +0,0 @@
1
- #
2
- # This helper method can be used in temp/pressure/humidity sensor examples.
3
- # Give a hash with readings as float values and it prints them neatly.
4
- #
5
- def print_tph_reading(reading)
6
- elements = []
7
-
8
- # Temperature
9
- if reading[:temperature]
10
- formatted_temp = reading[:temperature].to_f.round(2).to_s.ljust(5, '0')
11
- elements << "Temperature: #{formatted_temp} \xC2\xB0C"
12
- end
13
-
14
- # Pressure
15
- if reading[:pressure]
16
- formatted_pressure = reading[:pressure].round(2).to_s.ljust(7, '0')
17
- elements << "Pressure: #{formatted_pressure} Pa"
18
- end
19
-
20
- # Humidity
21
- if reading[:humidity]
22
- formatted_humidity = reading[:humidity].round(2).to_s.ljust(5, '0')
23
- elements << "Humidity: #{formatted_humidity} %"
24
- end
25
-
26
- return if elements.empty?
27
-
28
- # Time
29
- print "#{Time.now.strftime '%Y-%m-%d %H:%M:%S'} - "
30
-
31
- puts elements.join(" | ")
32
- end
@@ -1,51 +0,0 @@
1
- #
2
- # Example using QMP6988 sensor over I2C, for air temperature and pressure.
3
- #
4
- require 'bundler/setup'
5
- require 'denko'
6
- require_relative 'neat_tph_readings'
7
-
8
- board = Denko::Board.new(Denko::Connection::Serial.new)
9
- bus = Denko::I2C::Bus.new(board: board)
10
- sensor = Denko::Sensor::QMP6988.new(bus: bus) # address: 0x70 default
11
-
12
- # Verify chip_id.
13
- print "I2C device has chip ID: 0x#{sensor.chip_id.to_s(16).upcase}. "
14
- if sensor.chip_id == 0x5C
15
- puts "This matches the QMP6988."
16
- else
17
- puts "This does not match the QMP6988."
18
- end
19
- puts
20
-
21
- #
22
- # Change measurement settings:
23
- # temperature_samples can be 1,2,4,8,16,32 or 64 (default: 1)
24
- # pressure_samples can be 1,2,4,8,16,32 or 64 (default: 1)
25
- # iir_coefficient can be 0,2,4,8,16 or 32 (default: 0)
26
- #
27
- # High accuracy settings from datasheet, with IIR of 2.
28
- sensor.temperature_samples = 2
29
- sensor.pressure_samples = 16
30
- sensor.iir_coefficient = 2
31
-
32
- #
33
- # Change mode (default: forced_mode)
34
- #
35
- # Buggy on ESP32S3 in forced mode. Data registers return zeroes on all but first read.
36
- # Can't recreate on ESP32 V1, AVR or SAMD21. Put it in contiuous mode just in case.
37
- sensor.continuous_mode
38
- # sensor.forced_mode
39
-
40
- #
41
- # Set standby time (between measurements) for continuous mode only:
42
- # standby_time (given in ms) can be 1,5,20,250,500,1000,2000 or 4000 (default: 1)
43
- #
44
- # sensor.standby_time = 500
45
-
46
- # Poll it and print readings.
47
- sensor.poll(5) do |reading|
48
- print_tph_reading(reading)
49
- end
50
-
51
- sleep
@@ -1,15 +0,0 @@
1
- #
2
- # RCWL-9620 sensor over I2C to measure distance.
3
- #
4
- require 'bundler/setup'
5
- require 'denko'
6
-
7
- board = Denko::Board.new(Denko::Connection::Serial.new)
8
- bus = Denko::I2C::Bus.new(board: board)
9
- sensor = Denko::Sensor::RCWL9620.new(bus: bus) # address: 0x57 default
10
-
11
- sensor.poll(1) do |distance|
12
- puts "Distance is #{distance} mm"
13
- end
14
-
15
- sleep
@@ -1,32 +0,0 @@
1
- #
2
- # SHT30/31/35 sensor over I2C, for temperature and humidity.
3
- #
4
- require 'bundler/setup'
5
- require 'denko'
6
- require_relative 'neat_tph_readings'
7
-
8
- board = Denko::Board.new(Denko::Connection::Serial.new)
9
- bus = Denko::I2C::Bus.new(board: board)
10
- sensor = Denko::Sensor::SHT3X.new(bus: bus) # address: 0x44 default
11
-
12
- # Heater control
13
- sensor.heater_on
14
- puts "Heater on: #{sensor.heater_on?}"
15
- sensor.heater_off
16
- puts "Heater off: #{sensor.heater_off?}"
17
-
18
- # Reset (turns heater off)
19
- sensor.reset
20
- puts "Resetting..."
21
- puts "Heater off: #{sensor.heater_off?}"
22
- puts
23
-
24
- # Set repeatability= :low, :medium or :high (default). See datasheet for details.
25
- sensor.repeatability = :high
26
-
27
- # Poll it and print readings.
28
- sensor.poll(5) do |reading|
29
- print_tph_reading(reading)
30
- end
31
-
32
- sleep
@@ -1,46 +0,0 @@
1
- require 'bundler/setup'
2
- require 'denko'
3
-
4
- SPI_BIT_BANG_PINS = { clock: 13, input: 12, output: 11 }
5
- SELECT_PIN = 10
6
-
7
- board = Denko::Board.new(Denko::Connection::Serial.new)
8
- bus = Denko::SPI::BitBang.new(board: board, pins: SPI_BIT_BANG_PINS)
9
-
10
- # Methods so results print neatly. SPI transfers don't block main thread.
11
- def stop_waiting
12
- $waiting = false
13
- end
14
-
15
- def wait_for_read
16
- $waiting = true
17
- sleep 0.001 while $waiting
18
- end
19
-
20
- # Create a simple test component class.
21
- class SPITester
22
- include Denko::SPI::Peripheral::SinglePin
23
- end
24
- spi_tester = SPITester.new(bus: bus, pin: SELECT_PIN)
25
-
26
- spi_tester.add_callback do |rx_bytes|
27
- # If MOSI and MISO are connected this should match TEST_DATA.
28
- # If not, all bytes should be 255.
29
- puts "Result : #{rx_bytes.inspect}"
30
- stop_waiting
31
- end
32
-
33
- TEST_DATA = [0, 1, 2, 3, 4, 5, 6, 7]
34
-
35
- # Send and receive same data.
36
- puts "Tx 8 / Rx 8 : #{TEST_DATA.inspect}"
37
- spi_tester.spi_transfer(write: TEST_DATA, read: 8)
38
- wait_for_read
39
-
40
- puts "Tx 8 / Rx 12: #{TEST_DATA.inspect}"
41
- spi_tester.spi_transfer(write: TEST_DATA, read: 12)
42
- wait_for_read
43
-
44
- puts "Tx 8 / Rx 4 : #{TEST_DATA.inspect}"
45
- spi_tester.spi_transfer(write: TEST_DATA, read: 4)
46
- wait_for_read
@@ -1,40 +0,0 @@
1
- #
2
- # Button connected through input shift register (CD4021B).
3
- #
4
- require 'bundler/setup'
5
- require 'denko'
6
-
7
- # SPI pins (on board)
8
- SPI_BIT_BANG_PINS = { clock: 13, input: 12 }
9
- REGISTER_SELECT_PIN = 9
10
-
11
- # Button pin (on register parallel outputs)
12
- BUTTON_PIN = 0
13
-
14
- board = Denko::Board.new(Denko::Connection::Serial.new)
15
- bus = Denko::SPI::BitBang.new(board: board, pins: SPI_BIT_BANG_PINS)
16
-
17
- # InputRegister needs a bus and its select pin. The CD4021 likes SPI mode 2.
18
- # Other options and their defaults:
19
- # bytes: 1 - For daisy-chaining registers
20
- # spi_frequency: 1000000 - Only affects hardware SPI interfaces
21
- # spi_mode: 0
22
- # spi_bit_order: :msbfirst
23
- #
24
- register = Denko::SPI::InputRegister.new(bus: bus, pin: REGISTER_SELECT_PIN, spi_mode: 2)
25
-
26
- # InputRegister is a BoardProxy, so DigitalInput components can can use
27
- # it in place of a board. Use it with a Button instance.
28
- #
29
- # button starts listening automatically, which tells register to start listening.
30
- # On Denko::Board, InputRegisters listen with an 8ms interval by default, compared
31
- # to 1ms default for a Button directly connected to a Board.
32
- #
33
- button = Denko::DigitalIO::Button.new(pin: BUTTON_PIN, board: register)
34
-
35
- # Button callbacks.
36
- button.down { puts "Button pressed" }
37
- button.up { puts "Button released" }
38
-
39
- # Keep main thread alive.
40
- sleep
@@ -1,41 +0,0 @@
1
- #
2
- # Example of LED connected through an output shift register (74HC595).
3
- # Can be used over either a bit bang or hardware SPI interface.
4
- #
5
- require 'bundler/setup'
6
- require 'denko'
7
-
8
- # SPI pins (on board)
9
- SPI_BIT_BANG_PINS = { clock: 13, output: 11 }
10
- REGISTER_SELECT_PIN = 10
11
-
12
- # LED pin (on register parallel outputs)
13
- LED_PIN = 0
14
-
15
- # Works on hardware or bit-bang SPI.
16
- board = Denko::Board.new(Denko::Connection::Serial.new)
17
- # bus = Denko::SPI::Bus.new(board: board)
18
- bus = Denko::SPI::BitBang.new(board: board, pins: SPI_BIT_BANG_PINS)
19
-
20
- # OutputRegister needs a bus and its select pin.
21
- # Other options and their defaults:
22
- # bytes: 1 - For daisy-chaining registers
23
- # spi_frequency: 1000000 - Only affects hardware SPI interfaces
24
- # spi_mode: 0
25
- # spi_bit_order: :msbfirst
26
- #
27
- register = Denko::SPI::OutputRegister.new(bus: bus, pin: REGISTER_SELECT_PIN)
28
-
29
- # Turn the LED on by setting the corresponding register bit to 1, then writing to it.
30
- register.bit_set(LED_PIN, 1)
31
- register.write
32
- sleep 2
33
-
34
- # OutputRegister is a BoardProxy, so DigitalOutput components can use
35
- # it in place of a board. Use it with an LED instance.
36
- #
37
- led = Denko::LED.new(board: register, pin: LED_PIN)
38
-
39
- # Blink the LED and sleep the main thread.
40
- led.blink 0.5
41
- sleep
@@ -1,28 +0,0 @@
1
- #
2
- # SevenSegment LED driven though a pair of daisy chained OutputRegisters
3
- # (74HC595), with some segments on each register.
4
- #
5
- require 'bundler/setup'
6
- require 'denko'
7
-
8
- # SPI pins (on board)
9
- SPI_BIT_BANG_PINS = { clock: 13, output: 11 }
10
- REGISTER_SELECT_PIN = 10
11
-
12
- # SevenSegment pins (on registers' parallel outputs)
13
- SEVEN_SEGMENT_PINS = { cathode: 14, a: 10, b: 9, c: 4, d: 2, e: 1, f: 12, g: 13 }
14
-
15
- board = Denko::Board.new(Denko::Connection::Serial.new)
16
- bus = Denko::SPI::BitBang.new(board: board, pins: SPI_BIT_BANG_PINS)
17
-
18
- # Model as one 2-byte (16-bit) OutputRegister, since they're daisy chained.
19
- # Bit numbering starts at the first register, so bit 0 of the second register
20
- # is bit 8 of the OutputRegister instance.
21
- register = Denko::SPI::OutputRegister.new(bus: bus, pin: REGISTER_SELECT_PIN, bytes: 2)
22
- ssd = Denko::LED::SevenSegment.new(board: register, pins: SEVEN_SEGMENT_PINS)
23
-
24
- # Turn off the ssd on exit.
25
- trap("SIGINT") { exit !ssd.off }
26
-
27
- # Type a character and press Enter to show it on the SevenSegment LED.
28
- loop { ssd.display(gets.chomp) }
@@ -1,40 +0,0 @@
1
- #
2
- # 2 SPI registers (74HC595 and CD4021B) on the same bus, both acting as
3
- # BoardProxies for their Subcomponents.
4
- #
5
- require 'bundler/setup'
6
- require 'denko'
7
-
8
- # SPI pins (on board)
9
- SPI_BIT_BANG_PINS = { clock: 13, input: 12, output: 11 }
10
- OUT_REGISTER_SELECT = 10
11
- IN_REGISTER_SELECT = 9
12
-
13
- # LED and Button pins (on their respective registers' parallel pins)
14
- LED_PIN = 0
15
- BUTTON_PIN = 0
16
-
17
- board = Denko::Board.new(Denko::Connection::Serial.new)
18
- bus = Denko::SPI::BitBang.new(board: board, pins: SPI_BIT_BANG_PINS)
19
- out_register = Denko::SPI::OutputRegister.new(bus: bus, pin: OUT_REGISTER_SELECT)
20
- in_register = Denko::SPI::InputRegister.new(bus: bus, pin: IN_REGISTER_SELECT, spi_mode: 2)
21
-
22
- # LED connected to the output register.
23
- led = Denko::LED.new(board: out_register, pin: LED_PIN)
24
-
25
- # Button connected to the input register.
26
- button = Denko::DigitalIO::Button.new(board: in_register, pin: BUTTON_PIN)
27
-
28
- # Button callbacks.
29
- button.down do
30
- led.on
31
- puts "Button pressed"
32
- end
33
-
34
- button.up do
35
- led.off
36
- puts "Button released"
37
- end
38
-
39
- # Keep main thread alive.
40
- sleep
@@ -1,25 +0,0 @@
1
- #
2
- # Bit-bang UART is only used on the Arduino UNO / ATmega328p, because it has no
3
- # extra hardware UARTs. This also makes a self-loopback test impossible. Connect a
4
- # second board (Arduino Mega) and use its UART1 to verify bit-bang works on the Uno.
5
- #
6
- require 'bundler/setup'
7
- require 'denko'
8
-
9
- uno = Denko::Board.new(Denko::Connection::Serial.new(device: "/dev/cu.usbserial-1450"))
10
- mega = Denko::Board.new(Denko::Connection::Serial.new(device: "/dev/cu.usbmodem14101"))
11
-
12
- mega_uart1 = Denko::UART::Hardware.new(board: mega, index: 1, baud: 31250)
13
- uno_bb_uart = Denko::UART::BitBang.new(board: uno, pins: { rx:10, tx:11 }, baud: 31250)
14
-
15
- # Write to Uno, read from Mega
16
- uno_bb_uart.write("Hello World!\n")
17
- sleep 0.5
18
- line = mega_uart1.gets
19
- puts "Mega received from Uno: #{line}"
20
-
21
- # Write to Uno, read from Mega
22
- mega_uart1.write("Goodbye World!\n")
23
- sleep 0.5
24
- line = uno_bb_uart.gets
25
- puts "Uno received from Mega: #{line}"
@@ -1,40 +0,0 @@
1
- #
2
- # Example using one board's UART as the transport for a second board, also running Denko.
3
- #
4
- # For this example, board1 (direct) is an Arduino Mega. board2 (passthrough) is an Uno,
5
- # running Denko, with its UART pins (0, 1) connected to the Mega's UART1 pins (18, 19)
6
- #
7
- # This isn't 100% reliable. The Rx buffer on the direct board is periodically read, instead
8
- # of using an interrupt. It can potentially overflow, causing data from the passthrough board
9
- # to be lost. If flow control data is lost, the connection will halt.
10
- #
11
- # Use at your own risk, but for the best possible performance:
12
- # 1) Avoid long running commands on the direct board (eg. IR output, WS2812 etc.).
13
- # These block the CPU enough that the Rx buffer may not be read in time to avoid overflow.
14
- # 2) Use the lowest practical baud rate on the passthrough board, so the direct board's Rx
15
- # buffer takes as long as possible to fill, reducing the chance of data loss.
16
- #
17
- require 'bundler/setup'
18
- require 'denko'
19
-
20
- board1 = Denko::Board.new(Denko::Connection::Serial.new)
21
-
22
- uart = Denko::UART::Hardware.new(board: board1, index: 1)
23
- board2 = Denko::Board.new(Denko::Connection::BoardUART.new(uart, baud: 115200))
24
-
25
- led1 = Denko::LED.new(board: board1, pin: 13)
26
- led1.blink(0.5)
27
-
28
- led2 = Denko::LED.new(board: board2, pin: 13)
29
- led2.blink(0.5)
30
-
31
- input = Denko::AnalogIO::Input.new(board: board2, pin: :A0)
32
- input.smoothing = true
33
- input.smoothing_size = 10
34
- # Show input value only if it differs from previous state.
35
- input.add_callback do |value|
36
- puts "A0 value: #{value}" if value != input.state
37
- end
38
- input.listen(16)
39
-
40
- sleep
@@ -1,16 +0,0 @@
1
- #
2
- # Write to Tx pin of hardware UART1 and read data back on its Rx pin.
3
- #
4
- require 'bundler/setup'
5
- require 'denko'
6
-
7
- board = Denko::Board.new(Denko::Connection::Serial.new)
8
-
9
- uart = Denko::UART::Hardware.new(board: board, index: 1, baud: 31250)
10
-
11
- uart.write("Hello World!\nBye World!\n")
12
-
13
- sleep 1
14
-
15
- puts uart.gets
16
- puts uart.gets
@@ -1,72 +0,0 @@
1
- module Denko
2
- module EEPROM
3
- class BuiltIn
4
- include Behaviors::Component
5
- include Behaviors::Reader
6
- include Behaviors::Lifecycle
7
-
8
- public :state=
9
-
10
- def pin
11
- 254
12
- end
13
-
14
- after_initialize do
15
- load
16
- end
17
-
18
- def state
19
- state_mutex.synchronize { @state ||= Array.new(board.eeprom_length, nil) }
20
- end
21
-
22
- def load
23
- state.each_slice(128).with_index do |slice, index|
24
- read_using -> { board.eeprom_read(index * 128, slice.length) }
25
- end
26
- end
27
-
28
- def save
29
- state_mutex.synchronize do
30
- @state.each_slice(128).with_index do |slice, index|
31
- board.eeprom_write(index * 128, slice)
32
- end
33
- end
34
- load
35
- end
36
-
37
- #
38
- # Specific Array-like methods for convenience.
39
- #
40
- def length; board.eeprom_length; end
41
- alias :count :length
42
-
43
- def [](index)
44
- state_mutex.synchronize { @state.send :[], index }
45
- end
46
-
47
- def []=(index, value)
48
- state_mutex.synchronize { @state.send :[]=, index, value }
49
- end
50
-
51
- def each(&block)
52
- state_mutex.synchronize { @state.send :each, &block }
53
- end
54
-
55
- def each_with_index(&block)
56
- state_mutex.synchronize { @state.send :each_with_index, &block }
57
- end
58
-
59
- def pre_callback_filter(message)
60
- address = message.split("-", 2)[0].to_i
61
- bytes = message.split("-", 2)[1].split(",").map(&:to_i)
62
- {address: address, data: bytes}
63
- end
64
-
65
- def update_state(hash)
66
- state_mutex.synchronize do
67
- @state[hash[:address], hash[:data].length] = hash[:data]
68
- end
69
- end
70
- end
71
- end
72
- end