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
@@ -0,0 +1,306 @@
1
+ require_relative '../test_helper'
2
+
3
+ class CanvasTest < Minitest::Test
4
+ WIDTH = 32
5
+ HEIGHT = 32
6
+ FB_LENGTH = (WIDTH * HEIGHT / 8)
7
+
8
+ def subject
9
+ @subject ||= Denko::Display::Canvas.new(WIDTH, HEIGHT, colors: 2)
10
+ end
11
+
12
+ def test_framebuffer_default_state
13
+ assert_equal 2, subject.framebuffers.length
14
+ assert_equal subject.framebuffer.object_id, subject.framebuffers.first.object_id
15
+ assert_equal Array.new(FB_LENGTH) { 0x00 }, subject.framebuffers[0]
16
+ assert_equal Array.new(FB_LENGTH) { 0x00 }, subject.framebuffers[1]
17
+ end
18
+
19
+ def test_transform_default_state
20
+ refute subject.instance_variable_get(:@swap_xy)
21
+ refute subject.instance_variable_get(:@invert_x)
22
+ refute subject.instance_variable_get(:@invert_y)
23
+ assert_equal 0, subject.instance_variable_get(:@rotation)
24
+ end
25
+
26
+ def test_drawing_default_state
27
+ assert_equal 1, subject.current_color
28
+ assert_equal 1, subject.font_scale
29
+ end
30
+
31
+ def setup_test_pixels
32
+ subject.set_pixel x: 0, y: 0, color: 1
33
+ subject.set_pixel x: 1, y: 1, color: 2
34
+ subject.set_pixel x: 14, y: 14, color: 1
35
+ subject.set_pixel x: 15, y: 15, color: 2
36
+ end
37
+
38
+ def test_clear
39
+ setup_test_pixels
40
+ empty = Array.new(FB_LENGTH) { 0x00 }
41
+
42
+ subject.clear
43
+ assert_equal empty, subject.framebuffers[0]
44
+ assert_equal empty, subject.framebuffers[1]
45
+ end
46
+
47
+ def test_fill
48
+ empty = Array.new(FB_LENGTH) { 0x00 }
49
+ filled = Array.new(FB_LENGTH) { 0xFF }
50
+
51
+ subject.fill
52
+ assert_equal filled, subject.framebuffers[0]
53
+ assert_equal empty, subject.framebuffers[1]
54
+ end
55
+
56
+ def test_set_pixel
57
+ setup_test_pixels
58
+
59
+ fb0 = Array.new(FB_LENGTH) { 0 }
60
+ fb0[0] = 0b00000001
61
+ fb0[WIDTH+14] = 0b01000000
62
+ assert_equal fb0, subject.framebuffers[0]
63
+
64
+ fb1 = Array.new(FB_LENGTH) { 0 }
65
+ fb1[1] = 0b00000010
66
+ fb1[WIDTH+15] = 0b10000000
67
+ assert_equal fb1, subject.framebuffers[1]
68
+
69
+ # Setting a pixel in FB 0 (color: 1) clears corresponding bit in FB 1 (color: 2).
70
+ subject.set_pixel x: 1, y: 1, color: 1
71
+ assert_equal 0b00000010, subject.framebuffers[0][1]
72
+ assert_equal 0b00000000, subject.framebuffers[1][1]
73
+
74
+ # Setting a pixel to color: 0 clears it in all FBs.
75
+ subject.set_pixel x: 0, y: 0, color: 0
76
+ assert_equal 0b00000000, subject.framebuffers[0][0]
77
+ assert_equal 0b00000000, subject.framebuffers[1][0]
78
+
79
+ subject.set_pixel x: 15, y: 15, color: 0
80
+ assert_equal 0b00000000, subject.framebuffers[0][WIDTH+15]
81
+ assert_equal 0b00000000, subject.framebuffers[1][WIDTH+15]
82
+ end
83
+
84
+ def test_current_color
85
+ subject.current_color = 2
86
+ subject.set_pixel x: 0, y: 0
87
+ assert_equal 0b00000000, subject.framebuffers[0][0]
88
+ assert_equal 0b00000001, subject.framebuffers[1][0]
89
+ end
90
+
91
+ def test_get_pixel
92
+ setup_test_pixels
93
+ assert_equal 1, subject.get_pixel(x: 0, y: 0)
94
+ assert_equal 2, subject.get_pixel(x: 15, y: 15)
95
+ assert_equal 0, subject.get_pixel(x: 20, y: 20)
96
+ end
97
+
98
+ def test_line_single_pixel
99
+ subject.line x1: 5, y1: 5, x2: 5, y2: 5
100
+
101
+ fb0 = Array.new(FB_LENGTH) { 0 }
102
+ fb0[5] = 0b00100000
103
+
104
+ assert_equal fb0, subject.framebuffers[0]
105
+ end
106
+
107
+ LINE_HORIZONTAL_FB = [0, 0, 32, 32, 32, 32, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
108
+
109
+ def test_line_horizontal
110
+ subject.line x1: 2, y1: 5, x2: 6, y2: 5
111
+ assert_equal LINE_HORIZONTAL_FB, subject.framebuffers[0]
112
+ end
113
+
114
+ LINE_VERTICAL_FB = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
115
+
116
+ def test_line_vertical
117
+ subject.line x1: 10, y1: 2, x2: 10, y2: 6
118
+ assert_equal LINE_VERTICAL_FB, subject.framebuffers[0]
119
+ end
120
+
121
+ # 45 degree diagonal lines should produce the same result regardless of which is the start point (x1,y1).
122
+ LINE_45_FB = [0, 0, 0, 0, 0, 32, 64, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
123
+
124
+ def test_line_45
125
+ subject.line x1: 5, y1: 5, x2: 10, y2: 10
126
+ assert_equal LINE_45_FB, subject.framebuffers[0]
127
+ end
128
+
129
+ def test_line_45_swapped
130
+ subject.line x1: 10, y1: 10, x2: 5, y2: 5
131
+ assert_equal LINE_45_FB, subject.framebuffers[0]
132
+ end
133
+
134
+ # Non 45 degree diagonals are approximated slightly differently, depending on which point is x1,y1.
135
+ # This behavior might be useful.
136
+ # +ve gradients tend to "pull" down and to the left.
137
+ # -ve gradients tend to "pull" up and to the right.
138
+ LINE_DIAG_POS_FB = [0, 0, 0, 0, 0, 96, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
139
+
140
+ LINE_DIAG_NEG_FB = [0, 0, 0, 0, 0, 32, 64, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
141
+
142
+ def test_line_diagonal
143
+ # Note: run and rise not equal.
144
+ subject.line x1: 5, y1: 5, x2: 8, y2: 10
145
+ assert_equal LINE_DIAG_POS_FB, subject.framebuffers[0]
146
+ end
147
+
148
+ def test_line_diagonal_swapped
149
+ # Note: run and rise not equal.
150
+ subject.line x1: 8, y1: 10, x2: 5, y2: 5
151
+ assert_equal LINE_DIAG_NEG_FB, subject.framebuffers[0]
152
+ end
153
+
154
+ def test_line_knockout
155
+ inverted_fb = LINE_DIAG_POS_FB.map { |byte| ~byte & 0xFF }
156
+
157
+ subject.fill
158
+ subject.line x1: 5, y1: 5, x2: 8, y2: 10, color: 0
159
+ assert_equal inverted_fb, subject.framebuffers[0]
160
+ end
161
+
162
+ RECTANGLE_FB = [0, 0, 0, 0, 0, 224, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
163
+
164
+ def test_rectangle1
165
+ subject.rectangle x1: 5, y1: 5, x2: 19, y2: 14
166
+ assert_equal RECTANGLE_FB, subject.framebuffers[0]
167
+ end
168
+
169
+ def test_rectangle2
170
+ # w is x2-x1+1 (15) from previous, since first pixel counts
171
+ # h ix y2-y1+1 (10) from previous, since first pixel counts
172
+ subject.rectangle x: 5, y: 5, w: 15, h: 10
173
+ assert_equal RECTANGLE_FB, subject.framebuffers[0]
174
+ end
175
+
176
+ def test_rectangle3
177
+ # -ve values should produce the same rectangles
178
+ subject.rectangle x: 19, y: 14, w: -15, h: -10
179
+ assert_equal RECTANGLE_FB, subject.framebuffers[0]
180
+ end
181
+
182
+ SQUARES_FB = [0, 0, 0, 0, 0, 224, 224, 96, 96, 96, 96, 96, 96, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 127, 96, 96, 96, 96, 96, 96, 127, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
183
+
184
+ def test_layered_squares
185
+ subject.square x: 5, y: 5, size: 10, filled: true, color: 1
186
+ subject.square x: 7, y: 7, size: 6, filled: true, color: 0
187
+ assert_equal SQUARES_FB, subject.framebuffers[0]
188
+ end
189
+
190
+ PATH_FB = [3, 4, 24, 32, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 64, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 4, 8, 16, 32, 32, 64, 128, 128, 64, 48, 8, 4, 3, 0, 0, 1, 14, 112, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 28, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 56, 64]
191
+
192
+ def path_points
193
+ [ [0,0], [7,10], [15, 16], [23,5], [31, 30] ]
194
+ end
195
+
196
+ def test_path
197
+ subject.path path_points, color: 2
198
+ assert_equal PATH_FB, subject.framebuffers[1]
199
+ end
200
+
201
+ def test_path_knockout
202
+ inverted_fb = PATH_FB.map { |byte| ~byte & 0xFF }
203
+
204
+ subject.fill
205
+ subject.path path_points, color: 0
206
+ assert_equal inverted_fb, subject.framebuffers[0]
207
+ end
208
+
209
+ TRIANGLE_FB = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 96, 24, 6, 12, 48, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 96, 24, 6, 193, 240, 124, 31, 62, 248, 224, 131, 12, 48, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 96, 24, 6, 129, 224, 120, 31, 7, 1, 0, 0, 0, 0, 3, 15, 62, 240, 192, 3, 12, 48, 192, 0, 0, 0, 0, 0, 128, 224, 152, 134, 129, 128, 152, 158, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 159, 159, 159, 156, 128, 128, 131, 140, 176, 192]
210
+
211
+ def triangle_points
212
+ [
213
+ [ [1,31], [31,31], [16,1] ],
214
+ [ [7,28], [25,28], [16,8] ],
215
+ [ [11,24], [21,24], [16,13] ],
216
+ ]
217
+ end
218
+
219
+ def test_polygon
220
+ subject.polygon triangle_points[0]
221
+ subject.polygon triangle_points[1], filled: true
222
+ subject.polygon triangle_points[2], filled: true, color: 0
223
+ assert_equal TRIANGLE_FB, subject.framebuffers[0]
224
+ end
225
+
226
+ def test_triangle
227
+ t = triangle_points
228
+ subject.triangle x1: t[0][0][0], y1: t[0][0][1], x2: t[0][1][0], y2: t[0][1][1], x3: t[0][2][0], y3: t[0][2][1]
229
+ subject.triangle x1: t[1][0][0], y1: t[1][0][1], x2: t[1][1][0], y2: t[1][1][1], x3: t[1][2][0], y3: t[1][2][1], filled: true
230
+ subject.triangle x1: t[2][0][0], y1: t[2][0][1], x2: t[2][1][0], y2: t[2][1][1], x3: t[2][2][0], y3: t[2][2][1], filled: true, color: 0
231
+ assert_equal TRIANGLE_FB, subject.framebuffers[0]
232
+ end
233
+
234
+ ELLIPSE_CIRCLE_FB = [0, 0, 128, 192, 224, 240, 120, 60, 28, 30, 142, 142, 71, 71, 71, 71, 71, 71, 71, 142, 142, 30, 28, 60, 120, 240, 224, 192, 128, 0, 0, 0, 240, 254, 255, 15, 3, 224, 24, 4, 2, 1, 224, 24, 6, 2, 1, 1, 1, 2, 6, 24, 224, 1, 2, 4, 24, 224, 3, 15, 255, 254, 240, 0, 7, 63, 255, 248, 224, 131, 12, 16, 32, 64, 131, 140, 48, 32, 64, 64, 64, 32, 48, 140, 131, 64, 32, 16, 12, 131, 224, 248, 255, 63, 7, 0, 0, 0, 0, 1, 3, 7, 15, 30, 28, 60, 56, 56, 113, 113, 113, 113, 113, 113, 113, 56, 56, 60, 28, 30, 15, 7, 3, 1, 0, 0, 0, 0]
235
+
236
+ def test_ellipse_circle
237
+ subject.circle x: 15, y: 15, r: 15, filled: true
238
+ subject.circle x: 15, y: 15, r: 12, filled: true, color: 0
239
+ subject.ellipse x: 15, y: 15, a: 10, b: 9
240
+ subject.ellipse x: 15, y: 15, a: 5, b: 7
241
+ assert_equal ELLIPSE_CIRCLE_FB, subject.framebuffers[0]
242
+ end
243
+
244
+ TEXT_FB = [0, 0, 1, 1, 127, 1, 1, 0, 56, 84, 84, 84, 24, 0, 72, 84, 84, 84, 32, 0, 4, 63, 68, 64, 32, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 223, 223, 31, 223, 223, 255, 255, 127, 127, 127, 255, 255, 255, 127, 127, 127, 255, 255, 127, 31, 127, 255, 255, 255, 255, 255, 31, 255, 255, 255, 255, 255, 255, 255, 240, 255, 255, 255, 248, 245, 245, 245, 252, 255, 246, 245, 245, 245, 251, 255, 255, 248, 247, 247, 251, 255, 255, 255, 250, 255, 255, 255]
245
+
246
+ def test_text
247
+ # Regular text
248
+ subject.text_cursor = 1,7
249
+ subject.text "Test!"
250
+
251
+ # Fill bottom half
252
+ subject.rectangle x1: 0, y1: 16, x2: 31, y2: 31, filled: true
253
+
254
+ # Knockout text
255
+ subject.text_cursor = 1,28
256
+ subject.text "Test!", color: 0
257
+ assert_equal TEXT_FB, subject.framebuffers[0]
258
+ end
259
+
260
+ ROTATE_FB = [0, 124, 18, 17, 18, 124, 0, 127, 73, 73, 73, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 62, 34, 34, 20, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 40, 68, 68, 124, 68, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 72, 136, 72, 62, 0]
261
+
262
+ def test_rotate
263
+ 4.times do
264
+ subject.text_cursor = 0,7
265
+ subject.text "A"
266
+ subject.rotate(90)
267
+ end
268
+ subject.text_cursor = 6,7
269
+ subject.text "B"
270
+ assert_equal ROTATE_FB, subject.framebuffers[0]
271
+ end
272
+
273
+ REFLECT_FB = [0, 124, 18, 17, 18, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 41, 25, 9, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 144, 144, 144, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
274
+
275
+ def test_reflect
276
+ # Horizontally flipped, top right
277
+ subject.reflect(:x)
278
+ subject.text_cursor = 0,7
279
+ subject.text "R"
280
+ subject.reflect(:x)
281
+
282
+ # Vertically flipped, bottom left
283
+ subject.reflect(:y)
284
+ subject.text_cursor = 0,7
285
+ subject.text "P"
286
+ subject.reflect(:y)
287
+
288
+ # Restored
289
+ subject.text_cursor = 0,7
290
+ subject.text "A"
291
+ assert_equal REFLECT_FB, subject.framebuffers[0]
292
+ end
293
+
294
+ FONT_8X16_FB = [0, 0, 255, 255, 192, 192, 192, 192, 192, 192, 255, 255, 0, 0, 0, 0, 48, 48, 243, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 63, 63, 0, 0, 0, 0, 0, 0, 63, 63, 0, 0, 0, 0, 48, 48, 63, 63, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 24, 8, 8, 248, 8, 8, 24, 0, 8, 248, 136, 136, 232, 8, 16, 0, 0, 112, 136, 8, 8, 8, 56, 0, 24, 8, 8, 248, 8, 8, 24, 0, 0, 0, 32, 63, 32, 0, 0, 0, 32, 63, 32, 32, 35, 32, 24, 0, 0, 56, 32, 33, 33, 34, 28, 0, 0, 0, 32, 63, 32, 0, 0, 0]
295
+
296
+ def test_font
297
+ subject.text_cursor = 0,31
298
+ subject.font = :bmp_8x16
299
+ subject.text "TEST"
300
+
301
+ subject.text_cursor = 0,15
302
+ subject.font = :bmp_6x8
303
+ subject.font_scale = 2
304
+ subject.text "Hi!"
305
+ end
306
+ end
@@ -7,18 +7,45 @@ class HD44780Test < Minitest::Test
7
7
 
8
8
  def part
9
9
  @part ||= Denko::Display::HD44780.new cols: 16, rows: 2,
10
- board: board,
11
- pins: { rs: 8, enable: 9,
12
- d4: 4, d5: 5, d6: 6, d7: 7 }
10
+ board: board,
11
+ pins: {
12
+ rs: 8, enable: 9,
13
+ d4: 4, d5: 5, d6: 6, d7: 7
14
+ }
13
15
  end
14
-
16
+
15
17
  def test_pin_validation
16
18
  # Only given one pin out of d0-d3.
17
19
  assert_raises do
18
20
  Denko::Display::HD44780.new board: board, pins: { rs: 12, enable: 11,
19
- d4: 5, d5: 4, d6: 3, d7: 2, d3: 1 }
20
-
21
- end
21
+ d4: 5, d5: 4, d6: 3, d7: 2, d3: 1
22
+ }
23
+
24
+ end
25
+ end
26
+
27
+ def test_rw
28
+ @part = Denko::Display::HD44780.new cols: 16, rows: 2,
29
+ board: board,
30
+ pins: {
31
+ rs: 8, enable: 9, rw: 3,
32
+ d4: 4, d5: 5, d6: 6, d7: 7
33
+ }
34
+
35
+ assert_equal board.low, @part.rw.state
36
+ end
37
+
38
+
39
+ def test_backlight
40
+ @part = Denko::Display::HD44780.new cols: 16, rows: 2,
41
+ board: board,
42
+ pins: {
43
+ rs: 8, enable: 9, backlight: 3,
44
+ d4: 4, d5: 5, d6: 6, d7: 7
45
+ }
46
+
47
+ assert_equal Denko::LED::Base, @part.backlight.class
48
+ assert_equal board.high, @part.backlight.state
22
49
  end
23
50
 
24
51
  def test_write4
@@ -0,0 +1,45 @@
1
+ require_relative '../test_helper'
2
+
3
+ class BoardMock < Denko::Board
4
+ # Fake EEPROM
5
+ def eeprom_stub
6
+ @eeprom_stub ||= Array.new(eeprom_length){255}
7
+ end
8
+
9
+ def eeprom_read(start_address, length)
10
+ # Pack it up like a string coming from the board and update.
11
+ string = eeprom_stub[start_address, length].map{ |x| x.to_s }.join(",")
12
+ self.update("254:#{start_address}-#{string}\n")
13
+ end
14
+
15
+ def eeprom_write(start_address, bytes)
16
+ eeprom_stub[start_address, bytes.length] = bytes
17
+ end
18
+ end
19
+
20
+ class EEPROMBoardTest < Minitest::Test
21
+ def board
22
+ @board ||= BoardMock.new
23
+ end
24
+
25
+ def part
26
+ @part ||= board.eeprom
27
+ end
28
+
29
+ def test_pin_ee
30
+ assert_equal part.pin, 254
31
+ end
32
+
33
+ def test_individual_read_write
34
+ assert_equal 255, part[20]
35
+ part[20] = 111
36
+ assert_equal 111, part[20]
37
+ end
38
+
39
+ def test_range_read_write
40
+ data = [15, 23, 50, 12, 11]
41
+ index = 11
42
+ part[index] = data
43
+ assert_equal data, part[index..(index+data.length-1)]
44
+ end
45
+ end
@@ -0,0 +1,95 @@
1
+ # Minimal implementation of Minitest for mruby.
2
+ # Only handles #assert, #refute, and #assert_equal.
3
+ module Minitest
4
+ class AssertionError < StandardError
5
+ end
6
+
7
+ class Test
8
+ @@test_count = 0
9
+ @@failures = []
10
+ @@errors = []
11
+ @@assertions = 0
12
+
13
+ def self.run_all_tests
14
+ @@start_time = Time.now
15
+
16
+ test_classes = []
17
+ ObjectSpace.each_object(Class) do |klass|
18
+ test_classes << klass if (klass < Minitest::Test && klass != Minitest::Test)
19
+ end
20
+
21
+ test_classes.each { |c| c.run_tests }
22
+
23
+ @@finish_time = Time.now
24
+
25
+ report_results
26
+ end
27
+
28
+ def self.run_tests
29
+ test_methods = instance_methods.select { |method| method.to_s.start_with?("test_") }
30
+
31
+ test_methods.each do |test_method|
32
+ @@test_count += 1
33
+ test_instance = new
34
+
35
+ begin
36
+ test_instance.setup if test_instance.respond_to?(:setup)
37
+ test_instance.send(test_method)
38
+ print "."
39
+ rescue => e
40
+ if e.is_a?(AssertionError)
41
+ @@failures << "#{self.name}##{test_method}: #{e.message}"
42
+ print "F"
43
+ else
44
+ @@errors << "#{self.name}##{test_method}: #{e.class} - #{e.message}"
45
+ print "E"
46
+ end
47
+ ensure
48
+ begin
49
+ test_instance.teardown if test_instance.respond_to?(:teardown)
50
+ rescue => e
51
+ @@errors << "#{self.name}##{test_method} (teardown): #{e.class} - #{e.message}"
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ def self.report_results
58
+ puts "\n\nFinished in #{(@@finish_time - @@start_time).round(6)}s"
59
+
60
+ puts "#{@@test_count} tests, #{@@assertions} assertions, #{@@failures.length} failures, #{@@errors.length} errors"
61
+
62
+ unless @@failures.empty?
63
+ puts "\nFailures:"
64
+ @@failures.each_with_index do |failure, i|
65
+ puts "#{i + 1}) #{failure}"
66
+ end
67
+ end
68
+
69
+ unless @@errors.empty?
70
+ puts "\nErrors:"
71
+ @@errors.each_with_index do |error, i|
72
+ puts "#{i + 1}) #{error}"
73
+ end
74
+ end
75
+ end
76
+
77
+ def assert(condition, message = "Assertion failed")
78
+ @@assertions += 1
79
+ raise AssertionError.new(message) unless condition
80
+ end
81
+
82
+ def refute(condition, message = "Refutation failed - expected falsy value")
83
+ @@assertions += 1
84
+ raise AssertionError.new(message) if condition
85
+ end
86
+
87
+ def assert_equal(expected, actual, message = nil)
88
+ @@assertions += 1
89
+ unless expected == actual
90
+ msg = message || "Expected #{expected.inspect}, got #{actual.inspect}"
91
+ raise AssertionError.new(msg)
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,13 @@
1
+ # Stub out require_relative to avoid issues.
2
+ def require_relative(*args)
3
+ end
4
+
5
+ # Load minimal Minitest clone for mruby.
6
+ minitest_source = "#{File.dirname(__FILE__)}/mruby_minitest.rb"
7
+ eval(File.read(minitest_source))
8
+
9
+ # Test Display::Canvas.
10
+ canvas_test = "#{File.dirname(__FILE__)}/../display/canvas_test.rb"
11
+ eval(File.read(canvas_test))
12
+
13
+ Minitest::Test.run_all_tests
data/test/i2c/bus_test.rb CHANGED
@@ -9,74 +9,137 @@ class I2CBusTest < Minitest::Test
9
9
  @board ||= BoardMock.new
10
10
  end
11
11
 
12
- def part
13
- @part ||= Denko::I2C::Bus.new(board: board)
12
+ def bus
13
+ @bus ||= Denko::I2C::Bus.new(board: board)
14
14
  end
15
15
 
16
16
  def peripheral
17
- @peripheral ||= I2CPeripheralBase.new(bus: part, address: 0x30)
17
+ @peripheral ||= I2CPeripheralBase.new(bus: bus, address: 0x30)
18
18
  end
19
19
 
20
20
  def test_initialize
21
- assert_equal part.found_devices, []
22
- assert_equal 0, part.i2c_index
23
- refute_nil part.callbacks[:bus_controller]
21
+ assert_equal [], bus.found_devices
22
+ assert_equal 0, bus.i2c_index
23
+ refute_nil bus.callbacks[:bus_controller]
24
24
 
25
- part2 = Denko::I2C::Bus.new(board: board, index: 10)
26
- assert_equal 10, part2.i2c_index
27
-
28
- part3 = Denko::I2C::Bus.new(board: board, i2c_index: 11)
29
- assert_equal 11, part3.i2c_index
25
+ bus2 = Denko::I2C::Bus.new(board: board, index: 10)
26
+ assert_equal 10, bus2.i2c_index
30
27
  end
31
28
 
32
- def test_search
29
+ def test_search_result_string
33
30
  # Reject 0s created by leading and trailing colons.
34
- board.inject_read_for_i2c(0, ":48:50:")
31
+ board.inject_component_update(bus, ":48:50:")
32
+ bus.search
33
+ assert_equal [0x30, 0x32], bus.found_devices
34
+ end
35
35
 
36
- mock = Minitest::Mock.new.expect :call, nil, [0]
37
- board.stub(:i2c_search, mock) do
38
- part.search
39
- end
40
- mock.verify
36
+ def test_search_resul_array
37
+ board.inject_component_update(bus, [0, 0x30, 0x32])
38
+ bus.search
39
+ assert_equal [0x30, 0x32], bus.found_devices
40
+ end
41
41
 
42
- assert_equal part.found_devices, [0x30, 0x32]
42
+ def test_search_result_empty
43
+ board.inject_component_update(bus, ":")
44
+ bus.search
45
+ assert_equal [], bus.found_devices
43
46
  end
44
47
 
45
48
  def test_write
46
49
  mock = Minitest::Mock.new.expect :call, nil, [0, 0x30, [0x01, 0x02], 100000, false]
50
+
47
51
  board.stub(:i2c_write, mock) do
48
- part.write 0x30, [0x01, 0x02]
52
+ bus.write 0x30, [0x01, 0x02]
49
53
  end
50
54
  mock.verify
51
55
  end
52
56
 
53
- def test__read_string
54
- board.inject_read_for_i2c(0, "48-255,0,255,0,255,0")
57
+ def test_read_string
58
+ board.inject_component_update(bus, "48-255,0,255,0,255,0")
59
+ mock = Minitest::Mock.new.expect :call, nil, [0, 0x32, 0x03, 6, 100000, false]
55
60
 
61
+ board.stub(:i2c_read, mock) do
62
+ bus.read 0x32, 0x03, 6
63
+ end
64
+ mock.verify
65
+ end
66
+
67
+ def test_read_array
68
+ board.inject_component_update(bus, [48,255,0,255,0,255,0])
56
69
  mock = Minitest::Mock.new.expect :call, nil, [0, 0x32, 0x03, 6, 100000, false]
70
+
57
71
  board.stub(:i2c_read, mock) do
58
- part.read 0x32, 0x03, 6
72
+ bus.read 0x32, 0x03, 6
59
73
  end
60
74
  mock.verify
61
75
  end
62
76
 
63
- def test__read_array
64
- board.inject_read_for_i2c(0, [48,255,0,255,0,255,0])
77
+ def test_read_without_register
78
+ board.inject_component_update(bus, "48-255,127")
79
+ mock = Minitest::Mock.new.expect :call, nil, [0, 0x30, nil, 2, 100000, false]
65
80
 
66
- mock = Minitest::Mock.new.expect :call, nil, [0, 0x32, 0x03, 6, 100000, false]
67
81
  board.stub(:i2c_read, mock) do
68
- part.read 0x32, 0x03, 6
82
+ bus.read 0x30, nil, 2
83
+ end
84
+ mock.verify
85
+ end
86
+
87
+ def test_updates_peripherals_string
88
+ mock = Minitest::Mock.new.expect :call, nil, [[255, 127]]
89
+
90
+ peripheral.stub(:update, mock) do
91
+ bus.send(:update, "48-255,127")
92
+ bus.send(:update, "50-128,0")
69
93
  end
70
94
  mock.verify
71
95
  end
72
96
 
73
- def test_updates_peripherals
97
+ def test_updates_peripherals_array
74
98
  mock = Minitest::Mock.new.expect :call, nil, [[255, 127]]
75
99
 
76
100
  peripheral.stub(:update, mock) do
77
- part.send(:update, "48-255,127")
78
- part.send(:update, "50-128,0")
101
+ bus.send(:update, [0x30, 255, 127])
102
+ bus.send(:update, [0x32, 128, 0])
103
+ end
104
+ mock.verify
105
+ end
106
+
107
+ def test_ignores_updates_for_non_matching_addresses
108
+ # mock should not receive any calls
109
+ mock = Minitest::Mock.new
110
+
111
+ peripheral.stub(:update, mock) do
112
+ bus.send(:update, "49-255,127")
79
113
  end
80
114
  mock.verify
81
115
  end
116
+
117
+ def test_handles_empty_data_gracefully
118
+ bus.send(:update, "48-")
119
+ bus.send(:update, "48")
120
+ end
121
+
122
+ def test_handles_malformed_string_data
123
+ bus.send(:update, "invalid-data")
124
+ bus.send(:update, "")
125
+ bus.send(:update, nil)
126
+ end
127
+
128
+ def test_same_address_fails
129
+ peripheral
130
+ assert_raises { I2CPeripheralBase.new(bus: bus, address: 0x30) }
131
+ end
132
+
133
+ # Should split up Subcomponents behavior and test there?
134
+ def test_component_management
135
+ count = bus.components.length
136
+ new_peripheral = I2CPeripheralBase.new(bus: bus, address: 0x40)
137
+
138
+ assert_equal count+1, bus.components.length
139
+ assert_includes bus.components, new_peripheral
140
+ end
141
+
142
+ def test_found_devices_not_writable
143
+ assert_raises { bus.found_devices = nil }
144
+ end
82
145
  end