module main author unknown version 1 0 description '' script 63 82 { whenStarted OLEDInit_I2C 'OLED_0.96in' '3C' 0 false OLEDshowGDBuffer sendBroadcast 'display' } script 63 253 { whenBroadcastReceived 'display' forever { OLEDclear sayIt 'Rotary:' ('旋钮' 'A1') 'Button:' ('拨动开关' 'D4') graphIt ('旋钮' 'A1') ('拨动开关' 'D4') OLEDwrite 'Rotary:' 0 0 false OLEDwrite ('旋钮' 'A1') 0 15 false OLEDwrite 'Button :' 0 30 false OLEDwrite ('拨动开关' 'D4') 0 45 false waitMillis 200 } } module 'ESP32Uno 扩展板' author '邵悦' version 1 0 depends Tone 'Sensing/Temperature Humidity (DHT11, DHT22)' 'Sensing/IR Motion (PIR)' choices analogPinMenu '0' '1' '2' '3' '4' '5' choices aPinMenu A0 A1 A2 A3 choices digitalPinMenu '2' '3' '4' '5' '6' '7' '8' '9' '10' '11' '12' '13' choices dPinMenu D2 D3 D4 D5 D6 D7 D8 description 'Grove shield for Uno esp32 pin mapping minode sensor lib ' variables _wr32_analog_pins_list _wr32_pin_D4 _wr32_pin_A4 _wr32_pin_D3 _wr32_pin_A3 _wr32_pin_A2 _wr32_pin_A5 _wr32_pin_D7 _wr32_pin_D12 _wr32_initialized _wr32_digital_pins_list _wr32_pin_A0 _wr32_pin_D5 _wr32_pin_D8 _wr32_pin_D10 _wr32_pin_D9 _wr32_pin_A1 _wr32_pin_D13 _wr32_pin_D6 _wr32_pin_D2 _wr32_pin_D11 spec ' ' 'RGB灯' 'RGB彩灯 _ 接口 _' 'color menu.dPinMenu' nil 'D5' spec ' ' 'RGB灯关闭 端口 D' 'RGB彩灯关闭 接口 _' 'menu.dPinMenu' 'D5' space spec 'r' '旋钮' '旋钮 _' 'menu.aPinMenu' 'A1' spec 'r' '光线强度' '光线强度 _' 'menu.aPinMenu' 'A2' spec 'r' '麦克风' '麦克风 _' 'menu.aPinMenu' 'A3' space spec 'r' '拨动开关' '拨动开关 _' 'menu.dPinMenu' 'D2' spec 'r' '温度(摄氏度)' '温度(摄氏度) _' 'menu.dPinMenu' 'D3' spec 'r' '湿度' '湿度 _' 'menu.dPinMenu' 'D3' spec 'r' '红外检测PIR 在接口' '红外检测PIR 在接口 _ 检测到运动' 'menu.dPinMenu' 'D4' space spec ' ' '连接蜂鸣器' '连接蜂鸣器到 _' 'menu.dPinMenu' 'D6' spec ' ' '风扇 速度' '风扇 速度(0-100) _ 接口 _' 'auto menu.dPinMenu' '10' 'D7' spec ' ' '继电器' '继电器 在 _ 状态 _' 'menu.dPinMenu bool' 'D8' true space spec 'r' '参数处理' '_参数处理 _' 'auto' '10' spec ' ' 'minodeRGB' 'minodeRGB _ clkpin _ datapin _' 'color auto auto' nil 3 4 spec ' ' 'minodeRGB r' 'minodeRGB r _ g _ b _ clkpin _ datapin _' 'num num num auto auto' 255 0 0 3 4 spec ' ' '_wr32_init_pins' '_wr32_init_pins' spec 'r' 'wr32_GPIO_D' 'wr32_GPIO_D _' 'menu.digitalPinMenu' 2 spec 'r' 'wr32_GPIOPin_A' 'wr32_GPIO_A _' 'menu.analogPinMenu' 0 spec 'r' '_wr32 read analog A' 'wr32 read analog A _' 'menu.analogPinMenu' 0 spec 'r' 'wr32 read digital D' 'wr32 read digital D _' 'menu.digitalPinMenu' 2 spec ' ' 'wr32 set pin D' 'wr32 set pin D _ to _ (0-1023)' 'menu.digitalPinMenu num' '2' 100 spec ' ' 'wr32 set digital pin D' 'wr32 set digital pin D _ to _' 'menu.digitalPinMenu bool' 2 true to RGB灯 foo bar { minodeRGB foo (wr32_GPIO_D ('参数处理' bar)) (wr32_GPIO_D (('参数处理' bar) + 1)) } to 'RGB灯关闭 端口 D' foo { RGB灯 (0 + 0) foo } to '_wr32 read analog A' pin { return (analogReadOp (wr32_GPIOPin_A pin)) } to '_wr32_init_pins' { if _wr32_initialized {return} _wr32_pin_A0 = 2 _wr32_pin_A1 = 4 _wr32_pin_A2 = 35 _wr32_pin_A3 = 34 _wr32_pin_A4 = 36 _wr32_pin_A5 = 39 _wr32_pin_D2 = 26 _wr32_pin_D3 = 25 _wr32_pin_D4 = 17 _wr32_pin_D5 = 16 _wr32_pin_D6 = 27 _wr32_pin_D7 = 14 _wr32_pin_D8 = 12 _wr32_pin_D9 = 13 _wr32_pin_D10 = 5 _wr32_pin_D11 = 23 _wr32_pin_D12 = 19 _wr32_pin_D13 = 18 _wr32_analog_pins_list = ('[data:makeList]' _wr32_pin_A0 _wr32_pin_A1 _wr32_pin_A2 _wr32_pin_A3 _wr32_pin_A4 _wr32_pin_A5) _wr32_digital_pins_list = ('[data:makeList]' _wr32_pin_D2 _wr32_pin_D3 _wr32_pin_D4 _wr32_pin_D5 _wr32_pin_D6 _wr32_pin_D7 _wr32_pin_D8 _wr32_pin_D9 _wr32_pin_D10 _wr32_pin_D11 _wr32_pin_D12 _wr32_pin_D13) _wr32_initialized = (booleanConstant true) } to minodeRGB color clkpin datapin { 'minodeRGB r' (color >> 16) ((color % (1 << 16)) >> 8) (color % (1 << 8)) clkpin datapin } to 'minodeRGB r' r g b pin1 pin2 { local '_clkpin' pin1 local '_datapin' pin2 local '_rr' (r >> 1) local '_gg' (g >> 1) local '_bb' (b >> 1) local 'rgb' (hexToInt '808080') rgb = (rgb | (_gg << 15)) rgb = (rgb | (_rr << 7)) rgb = (rgb | _bb) repeat 32 { digitalWriteOp _datapin false digitalWriteOp _clkpin true digitalWriteOp _clkpin false } repeat 24 { if ((rgb & (hexToInt '800000')) != 0) { digitalWriteOp _datapin true } else { digitalWriteOp _datapin false } digitalWriteOp _clkpin true digitalWriteOp _clkpin false rgb = (rgb << 1) } rgb = (hexToInt '808080') repeat 24 { if ((rgb & (hexToInt '800000')) != 0) { digitalWriteOp _datapin true } else { digitalWriteOp _datapin false } digitalWriteOp _clkpin true digitalWriteOp _clkpin false rgb = (rgb << 1) } } to 'wr32 read digital D' pin { return (digitalReadOp (wr32_GPIO_D pin)) } to 'wr32 set digital pin D' pin value { digitalWriteOp (wr32_GPIO_D pin) value } to 'wr32 set pin D' pin value { analogWriteOp (wr32_GPIO_D pin) value } to wr32_GPIOPin_A pin { '_wr32_init_pins' comment 'pins A0~ A5, A0 is list 1' return (at (pin + 1) _wr32_analog_pins_list) } to wr32_GPIO_D pin { '_wr32_init_pins' comment 'pins A2~A13,A2 is list 1 ' return (at (pin - 1) _wr32_digital_pins_list) } to '光线强度' foo { return ('_wr32 read analog A' ('参数处理' foo)) } to '参数处理' foo { if (isType foo 'number') { return foo } ((size foo) == 2) { return (at 'last' foo) } else { return 99 } } to '拨动开关' foo { return ('wr32 read digital D' ('参数处理' foo)) } to '旋钮' foo { return ('_wr32 read analog A' ('参数处理' foo)) } to '温度(摄氏度)' foo { return (temperature_DHT11 (wr32_GPIO_D ('参数处理' foo))) } to '湿度' foo { return (humidity_DHT11 (wr32_GPIO_D ('参数处理' foo))) } to '红外检测PIR 在接口' pin { return (PIR (wr32_GPIO_D ('参数处理' pin))) } to '继电器' foo bar { 'wr32 set digital pin D' ('参数处理' foo) bar } to '连接蜂鸣器' foo { 'attach buzzer to pin' (wr32_GPIO_D ('参数处理' foo)) } to '风扇 速度' foo bar { 'wr32 set pin D' ('参数处理' bar) (foo * 10) } to '麦克风' foo { return ('_wr32 read analog A' ('参数处理' foo)) } module 'HTTP client' Comm author MicroBlocks version 1 5 depends WiFi tags http network get post put delete choices requestTypes GET POST PUT DELETE description 'Send HTTP requests and get responses back. You can use this library to fetch or store data in online servers, or to control other Wifi-enabled boards. ' spec 'r' '_http_body_start' '_http_body_start _' 'auto' '' spec 'r' '_http_header_text' '_http_header_text _' 'auto' '' spec 'r' '_http_status_text' '_http_status_text _' 'auto' '' spec 'r' '_http_header' '_header _ of response _' 'auto auto' 'content-length' '' spec 'r' '_http_status' '_status of response _' 'auto' '' spec 'r' '_line_end' '_line_end' spec 'r' '_lowercase' '_lowercase _' 'auto' 'ABC Def gH' spec 'r' '_request_url' '_request_url _ method _ body _ : port _' 'str menu.requestTypes str num' '' 'GET' '' 80 spec 'r' '_readHTTPResponse' '_readHTTPResponse' spec 'r' 'http܃//' 'http #SVG#colon-double-slash _ : port _' 'auto num' 'microblocks.fun/example.txt' 80 spec ' ' 'request' '_ data _ to http #SVG#colon-double-slash _ : port _' 'menu.requestTypes str str num' 'POST' 'MicroBlocks is fun' 'microblocks.fun' 80 to '_http_body_start' response { return (('[data:find]' ('[data:unicodeString]' ('[data:makeList]' 13 10 13 10)) response) + 4) } to '_http_header' header response { local 'header text' ('_http_header_text' response) header = ('_lowercase' header) repeatUntil ((size (v 'header text')) < 3) { local 'key' ('[data:copyFromTo]' (v 'header text') 1 (('[data:find]' ':' (v 'header text')) - 1)) if (('_lowercase' key) == header) { return ('[data:copyFromTo]' (v 'header text') (('[data:find]' ':' (v 'header text')) + 2) (('[data:find]' ('_line_end') (v 'header text')) - 1)) } 'header text' = ('[data:copyFromTo]' (v 'header text') (('[data:find]' ('_line_end') (v 'header text')) + 2)) } return (booleanConstant false) } to '_http_header_text' response { local 'headersStart' (('[data:find]' ('_line_end') response) + 2) return ('[data:copyFromTo]' response headersStart (('_http_body_start' response) + 2)) } to '_http_status' response { return (('[data:copyFromTo]' ('_http_status_text' response) 1 4) + 0) } to '_http_status_text' response { return ('[data:copyFromTo]' response ('[data:find]' ' ' response) ('[data:find]' ('_line_end') response)) } to '_line_end' { return ('[data:unicodeString]' ('[data:makeList]' 13 10)) } to '_lowercase' string { local 'lowercased' '' local 'char code' '' for c (size string) { 'char code' = ('[data:unicodeAt]' c string) if (and ((v 'char code') >= 65) ((v 'char code') <= 90)) { 'char code' = ((v 'char code') + 32) } lowercased = ('[data:join]' lowercased ('[data:unicodeString]' (v 'char code'))) } return lowercased } to '_readHTTPResponse' { local 'response' '' local 'lastChunkTime' (millisOp) repeatUntil (not ('[net:httpIsConnected]')) { local 'chunk' ('[net:httpResponse]') if ((size chunk) > 0) { response = ('[data:join]' response chunk) lastChunkTime = (millisOp) } (((millisOp) - lastChunkTime) > 10000) { return response } waitMillis 20 } response = ('[data:join]' response ('[net:httpResponse]')) return response } to '_request_url' url method body optionalPort { local 'port' 80 if ((pushArgCount) > 3) { port = optionalPort } local 'host' '' local 'path' '' local 'slashPosition' ('[data:find]' '/' url) if (slashPosition > 0) { host = ('[data:copyFromTo]' url 1 (slashPosition - 1)) path = ('[data:copyFromTo]' url (slashPosition + 1)) } else { host = url } if (('[net:wifiStatus]') != 'Connected') { return ('[data:join]' '0 Not Connected' ('_line_end')) } '[net:httpConnect]' host port if (not ('[net:httpIsConnected]')) { return ('[data:join]' '0 Could not connect to server' ('_line_end')) } '[net:httpRequest]' method host path body return ('_readHTTPResponse') } to 'http܃//' url optionalPort { local 'port' 80 if ((pushArgCount) > 1) { port = optionalPort } local 'response' ('_request_url' url 'GET' '' port) if (response != '') { if (('_http_status' response) == 200) { return ('[data:copyFromTo]' response ('_http_body_start' response)) } else { return ('_http_status_text' response) } } else { return 'Failed to get response' } } to request method data url optionalPort { local 'port' 80 if ((pushArgCount) > 3) { port = optionalPort } local 'response' ('_request_url' url method data port) if (response != '') { local 'status' ('_http_status' response) local 'body start' ('_http_body_start' response) if (and (200 == status) ((v 'body start') > 0)) { sayIt ('[data:copyFromTo]' response (v 'body start')) } else { sayIt ('_http_status_text' response) } } else { sayIt 'Failed to get response' } } module 'IR Motion (PIR)' Input author MicroBlocks version 1 1 tags sensor motion presence description 'Detect motion with a passive infrared (PIR) sensor.' variables _PIRs spec 'r' 'PIR' 'PIR at pin _ detected movement' 'auto' '10' spec 'r' '_PIR' '_PIR at pin _' 'auto' '10' spec ' ' '_pirLoop' '_pirLoop' to PIR pin { sendBroadcast '_pirLoop' waitUntil (_PIRs != 0) return (at 2 ('_PIR' pin)) } to '_PIR' pin { for PIR _PIRs { if ((at 1 PIR) == pin) {return PIR} } local 'PIR' ('[data:makeList]' pin (booleanConstant false)) '[data:addLast]' PIR _PIRs return PIR } to '_pirLoop' { if (_PIRs == 0) {_PIRs = ('[data:makeList]')} forever { for PIR _PIRs { if (digitalReadOp (at 1 PIR)) { if (not (at 2 PIR)) {atPut 2 PIR (booleanConstant true)} } else { if (at 2 PIR) {atPut 2 PIR (booleanConstant false)} } } waitMillis 5 } } module 'OLED Graphics' Output author 'Turgut Guneysu and Miroslav Klimos' version 1 22 choices DispTypeMenu 'OLED_0.96in' 'OLED_1.3in' 'OLED_1.54in' 'OLED_2.42in' choices OnOffMenu On Off choices VideoMenu Inverse Normal choices ModeMenu Horizontal Vertical description 'Supports 0.96in and 1.54in Displays with SSD1306 and 2.42in OLED with SSD1309 chipsets, also 1.3in with SHT1106 chipset. Comm mode is I2C or SPI. Changes: - always display buffer - defer display updates block - remove reveal - switch to x (0-127), y (0-63) - Pruned commands and vars - textX and textY - Text at any x and y - handles newLines - removed dependencies - unified data format for chars, sprites, and images - image draw at any x,y - filled rectangle - image draw bug fix - stack overflow fix - improve conversion ops, i2c io, rect fill, img processing, and buffer displays - reduce i2c buffer to 64 bytes - correct internal draws, clean-up vars, restore _clearDisplay Miroslav Klimos - added Zoom option for text - added support for 1.3in OLED (1106) - thanks to KwyjiboYue ' variables OLEDReady OLEDi2cAddr _GDBuffer _cDecTBL _cTABLE _comMode _dcPin _delayGDUpd _displayType _eol _resetPin _textX _textY _dataPrefix _byteCount _imgData _imgWidth _imgHeight _ColumnOffset spec ' ' 'OLEDInit_I2C_auto' 'initialize i2c(auto) reset pin# _ flip _' 'auto bool' '-' false spec ' ' 'OLEDInit_I2C' 'initialize i2c _ address(hex) _ reset pin# _ flip _' 'str.DispTypeMenu auto auto bool' 'OLED_0.96in' '3C' '-' false spec ' ' 'OLEDInit_SPI' 'initialize spi _ d/c pin# _ reset pin# _ flip _' 'str.DispTypeMenu auto auto bool' 'OLED_2.42in' 16 8 false spec ' ' 'OLEDwrite' 'write _ at x _ y _ inverse _ : scale _' 'auto num num bool num' 'Hello!' 0 0 false 1 spec ' ' 'OLEDshowGDBuffer' 'show display buffer' spec ' ' 'OLEDclear' 'clear' spec ' ' 'OLEDcontrast' 'set contrast (1-4) _' 'auto' 2 spec ' ' 'OLEDdrawCircle' 'draw circle at x _ y _ radius _ erase _ : filled _' 'auto auto auto bool bool' 64 32 '10' false false spec ' ' 'OLEDdrawImage' 'draw image _ at x _ y _' 'auto auto auto' 0 0 0 spec ' ' 'OLEDdrawLine' 'draw line from x _ y _ to x _ y _ erase _' 'auto auto auto auto bool any' 0 0 127 63 false spec ' ' 'OLEDdrawRect' 'draw rectangle x _ y _ w _ h _ erase _ rounding(3-15) _ : filled _' 'auto auto auto auto bool auto bool' 0 0 127 63 false 0 false spec ' ' 'OLEDfillRect' 'fill rectangle x _ y _ w _ h _ erase _' 'auto auto auto auto bool' 20 20 30 20 false spec ' ' '_OLEDflip' '_flip display top _' 'bool' false spec 'r' 'OLEDmakeImage' 'make image _' 'microbitDisplay' 33084991 spec ' ' 'OLEDpixel' 'set pixel x _ y _ erase _' 'auto auto bool' 0 0 false spec ' ' 'OLEDsetVideo' 'set video _' 'str.VideoMenu' 'Inverse' spec 'r' 'OLEDwru' 'cursor location' spec ' ' 'defer display updates' 'defer display updates' spec ' ' '_GDDRAMoff' '_GDDRAMoff' spec ' ' '_GDDRAMon' '_GDDRAMon' spec ' ' '_OLEDcursorReset' '_cursor reset' spec ' ' '_OLEDsetDisplay' '_set display _' 'str.OnOffMenu' 'On' spec ' ' '_OLEDreset' '_set reset Pin# _' 'auto' '0' spec ' ' '_SPIWriteCmd' '_SPIWriteCmd' spec ' ' '_SPIWriteData' '_SPIWriteData' spec ' ' '_clearDisplay' '_clearDisplay' spec ' ' '_corner' '_corner _ _ _ _ _ _' 'auto auto auto auto bool bool' 'TL' 32 1 0 false false spec 'r' '_dec2hex' '_dec2hex _' 'auto' 0 spec ' ' '_initCheck' '_initCheck' spec ' ' '_initDisplayHW' '_initDisplayHW' spec ' ' '_initLibrary' '_initLibrary' spec ' ' '_process image data' '_process image data _' 'auto' ' ' spec ' ' '_sendCmd' '_sendCmd _' 'auto' '' spec ' ' '_verifyXY' '_verifyXY _ _' 'auto auto' 0 0 spec ' ' '_OLEDwriteChar' '_write char _ at x _ y _ inverse _ scale _' 'auto auto auto bool num' 'A' 1 1 false 1 to OLEDInit_I2C dispType i2cAddr resetPin flip { comment 'Sets display type and interface and initializes HW settings. Do NOT set a ResetPin# unless one exists on the display hardware. NOTE: Displays supported: - OLED 0.96in (SD1306 chip - displayType=3) and - OLED 1.3in (SH1106 chip- displayType=8) and - OLED 1.54in (SD1309 chip - displayType=6) and - OLED 2.42" (SD1309 chip - displayType=6 ??? ) All displays are 128x64 pixels or 16x8 characters in size. Displays come in dual mode version (i2c and spi) and pure i2c. Pure i2c models do not have a Reset pin. flip setting controls display hardware write direction: - false: top to bottom - true: bottom to top' OLEDInit_I2C_auto resetPin flip } to OLEDInit_I2C_auto resetPin flip { comment 'Sets display type and interface and initializes HW settings. I2C addresss is checked for 0x3C and 0x3D automatically. Display typ is read from status register' _resetPin = resetPin _comMode = 'i2c' _ColumnOffset = '0x00' OLEDi2cAddr = 60 if (not ('[sensors:i2cExists]' OLEDi2cAddr)) { OLEDi2cAddr += 1 if (not ('[sensors:i2cExists]' OLEDi2cAddr)) { sayIt 'I2C OLED Display not found' waitMillis 2000 return 0 } } if (_displayType != 0) { return } local '_statusreg' 0 _statusreg = (newList 1 0) '[sensors:i2cWrite]' OLEDi2cAddr ('[data:newByteArray]' 1 0) '[sensors:i2cRead]' OLEDi2cAddr _statusreg _displayType = ((at 1 _statusreg) & 15) sayIt 'Display found at address: ' OLEDi2cAddr '- type:' _displayType if (_displayType == 8) { _ColumnOffset = '0x02' } '_initDisplayHW' if flip { '_OLEDflip' true } '_initLibrary' OLEDclear OLEDcontrast 4 } to OLEDInit_SPI dispType dcPin resetPin flip { comment 'Sets display type and interface and initializes HW settings. SPI four wire in Mode-0 is supported. Max speed=10000000. Do NOT set a ResetPin# unless one exists on the display hardware. NOTE: Displays supported: - OLED 0.96in (SD1306 chip) and - OLED 2.42" (SD1309 chip) Both displays are 128x64 pixels or 16x8 characters in size. Displays come in dual mode version (i2c and spi) and pure i2c. Pure i2c models do not have a Reset pin. flip setting controls display hardware write direction: - false: top to bottom - true: bottom to top' _comMode = 'spi' _dcPin = dcPin if (_displayType != 0) { return } _resetPin = resetPin if (dispType == 'OLED_0.96in') { _displayType = 3 } (dispType == 'OLED_1.3in') { _displayType = 8 } else { _displayType = 6 } '[sensors:spiSetup]' 10000000 '_initDisplayHW' if flip { '_OLEDflip' true } '_initLibrary' OLEDclear OLEDcontrast 4 } to OLEDclear { comment 'Set bounds to FullScreen and CLEARs display and GDBuffer.' _GDBuffer = ('[data:newByteArray]' 1024) '_initCheck' '_OLEDcursorReset' OLEDshowGDBuffer } to OLEDcontrast contrast { comment 'Sets the brightness control of the display to one of four values. 1 is the least bright, 4 is the brightest setting.' '_initCheck' local 'cLevels' ('[data:makeList]' 0 '1F' '2F' 'F0') if (and (contrast >= 1) (contrast <= 4)) { local 'i2cCmd' ('[data:join]' '81,' (at contrast cLevels)) '_sendCmd' i2cCmd } else { sayIt 'Error in CONTRAST Level' } } to OLEDdrawCircle cx cy r erase optionalFilled { comment 'Bresenham Circle: Draws circles, even partially bigger than the display. x: 0-127 y: 0-63 void plotCircle(int xm, int ym, int r) { int x = -r, y = 0, err = 2-2*r; /* II. Quadrant */ do { setPixel(xm-x, ym+y); /* I. Quadrant */ setPixel(xm-y, ym-x); /* II. Quadrant */ setPixel(xm+x, ym-y); /* III. Quadrant */ setPixel(xm+y, ym+x); /* IV. Quadrant */ r = err; if (r <= y) err += ++y*2+1; /* e_xy+e_y < 0 */ if (r > x || err > y) err += ++x*2+1; /* e_xy+e_x > 0 or no 2nd y-step */ } while (x < 0); }' '_initCheck' local 'oldDelayGDUpd' _delayGDUpd _delayGDUpd = (booleanConstant true) local 'filled' (argOrDefault 5 (booleanConstant false)) local 'x' (-1 * r) local 'y' 0 local 'err' (2 - (2 * r)) repeatUntil (x >= 0) { if filled { OLEDdrawLine (cx - x) (cy + y) (cx + x) (cy + y) erase OLEDdrawLine (cx + y) (cy + x) (cx - y) (cy + x) erase } else { OLEDpixel (cx - x) (cy + y) erase OLEDpixel (cx - y) (cy - x) erase OLEDpixel (cx + x) (cy - y) erase OLEDpixel (cx + y) (cy + x) erase } r = err if (r <= y) { y += 1 err = (err + ((y * 2) + 1)) } if (or (r > x) (err > y)) { x += 1 err = (err + ((x * 2) + 1)) } } _delayGDUpd = oldDelayGDUpd if (not _delayGDUpd) { OLEDshowGDBuffer } } to OLEDdrawImage image x y { comment '_imgHeight has to be mod8 or 1-8' if (0 == image) { return 0 } local 'oldDelayGDUpd' _delayGDUpd _delayGDUpd = (booleanConstant true) for row# (maximum 1 ((minimum _imgHeight (64 - y)) / 8)) { local 'GDidx' ((((y / 8) * 128) + x) + 1) local 'imgDispl' ((row# - 1) * _imgWidth) for byte# (minimum _imgWidth (128 - x)) { comment 'If y is on page boundary, just copy' if (0 == (y % 8)) { atPut GDidx _GDBuffer (at (imgDispl + byte#) image) GDidx += 1 if (GDidx > 1024) { _delayGDUpd = oldDelayGDUpd if (not _delayGDUpd) { OLEDshowGDBuffer } return 0 } } else { comment 'Else, copy GDLowBits + shiftedByte + GDHiBits to GDBuffer and GDBuffer +128' local 'shiftedByte' ('_dec2hex' ((at (imgDispl + byte#) image) << (y % 8))) local 'temp' 0 local 'tempHI' ('[data:copyFromTo]' shiftedByte 1 2) local 'tempLO' ('[data:copyFromTo]' shiftedByte 3 4) local 'GDLowBits' ((1 << (y % 8)) - 1) local 'GDHiBits' (255 - GDLowBits) tempLO = ((hexToInt tempLO) | ((at GDidx _GDBuffer) & GDLowBits)) atPut GDidx _GDBuffer tempLO if ((GDidx + 128) <= 1024) { tempHI = ((hexToInt tempHI) | ((at (GDidx + 128) _GDBuffer) & GDHiBits)) atPut (GDidx + 128) _GDBuffer tempHI } GDidx += 1 if (GDidx > 1024) { _delayGDUpd = oldDelayGDUpd if (not _delayGDUpd) { OLEDshowGDBuffer } return 0 } } waitMillis 0 } y += 8 } _delayGDUpd = oldDelayGDUpd if (not _delayGDUpd) { OLEDshowGDBuffer } } to OLEDdrawLine x0 y0 x1 y1 erase { comment 'Draws a line from x0,y0 to x1,y1 using the Bresenham Algorithm x: 0-127 y: 0-63 plotLine(int x0, int y0, int x1, int y1) dx = abs(x1-x0); sx = x0= dy) /* e_xy+e_x > 0 */ err += dy; x0 += sx; end if if (e2 <= dx) /* e_xy+e_y < 0 */ err += dx; y0 += sy; end if end while' '_initCheck' local 'oldDelayGDUpd' _delayGDUpd _delayGDUpd = (booleanConstant true) results = ('[data:makeList]') local 'dx' (absoluteValue (x1 - x0)) local 'dy' (-1 * (absoluteValue (y1 - y0))) local 'err' (dx + dy) local 'e2' 0 local 'done' (booleanConstant false) if (x0 < x1) { local 'sx' 1 } else { local 'sx' -1 } if (y0 < y1) { local 'sy' 1 } else { local 'sy' -1 } repeatUntil done { OLEDpixel x0 y0 erase if (and (x0 == x1) (y0 == y1)) { done = (booleanConstant true) } e2 = (2 * err) if (e2 >= dy) { err += dy x0 += sx } if (e2 <= dx) { err += dx y0 += sy } } _delayGDUpd = oldDelayGDUpd if (not _delayGDUpd) { OLEDshowGDBuffer } } to OLEDdrawRect TLx TLy width height erase cornerRad optionalFilled { comment 'Draw Rectangle with optional rounded corners with radius R. Does not check for reversed rectangle coordinates for round corners.' '_initCheck' local 'oldDelayGDUpd' _delayGDUpd _delayGDUpd = (booleanConstant true) local 'BRx' (TLx + width) local 'BRy' (TLy + height) local 'filled' (argOrDefault 7 (booleanConstant false)) if (cornerRad < 3) { cornerRad = 0 } else { '_corner' 'TL' TLx TLy cornerRad erase filled '_corner' 'TR' BRx TLy cornerRad erase filled '_corner' 'BR' BRx BRy cornerRad erase filled '_corner' 'BL' TLx BRy cornerRad erase filled } comment 'TOP' for f ('[data:range]' TLy (ifExpression filled (TLy + cornerRad) TLy)) { OLEDdrawLine (TLx + cornerRad) f (BRx - cornerRad) f erase } comment 'MIDDLE' if filled { for f ('[data:range]' ((TLy + 1) + cornerRad) ((BRy - 1) - cornerRad)) { OLEDdrawLine TLx f BRx f erase } } else { OLEDdrawLine TLx ((BRy - 1) - cornerRad) TLx ((TLy + 1) + cornerRad) erase OLEDdrawLine BRx ((TLy + 1) + cornerRad) BRx ((BRy - 1) - cornerRad) erase } comment 'BOTTOM' for f ('[data:range]' (ifExpression filled (BRy - cornerRad) BRy) BRy) { OLEDdrawLine (TLx + cornerRad) f (BRx - cornerRad) f erase } _delayGDUpd = oldDelayGDUpd if (not _delayGDUpd) { OLEDshowGDBuffer } } to OLEDfillRect x y w h erase { local 'oldDelayGDUpd' _delayGDUpd _delayGDUpd = (booleanConstant true) for row ('[data:range]' 0 (h - 1)) { for col ('[data:range]' 0 (w - 1)) { OLEDpixel (x + col) (y + row) erase } } _delayGDUpd = oldDelayGDUpd if (not _delayGDUpd) { OLEDshowGDBuffer } } to OLEDmakeImage spr# { comment 'Receives a spr# representing 5x5 image matrix. Converts it to a HOR array of 5 VERT bitmap numbers. This can be displayed with the draw image block. sprNum max is 33554431' '_initCheck' local 'spriteList' ('[data:newByteArray]' 5) local 'val' 0 for col 5 { for row ('[data:asByteArray]' ('[data:makeList]' 0 5 10 15 20)) { local 'bit' (col + row) local 'vertBit#' (bit / 5) if ((spr# & (1 << (bit - 1))) != 0) { if (col != 5) { val += (1 << vertBit#) } else { val += (1 << (vertBit# - 1)) } } } atPut col spriteList val val = 0 } _imgWidth = 5 _imgHeight = 5 return spriteList } to OLEDpixel x y erase { comment 'Places a pixel at x,y in the virtual GDBuffer Use OLEDshowGDBuffer to display it 8192 Pixels over 1024 Bytes x: 0-127 y: 0-63 page#: 0-7 pagePixel#: 0-7 GDIndex: 1-1024 RangeCheck: verify x and y are in range of display limits if X <= num <= Y' '_initCheck' local 'pagePixel#' (y % 8) local 'GDIndex' ((x + ((y / 8) * 128)) + 1) if (GDIndex <= 1024) { local 'byteBMP' (at GDIndex _GDBuffer) byteBMP = (ifExpression (not erase) (byteBMP | (1 << pagePixel#)) (byteBMP & ('~' (1 << pagePixel#)))) atPut GDIndex _GDBuffer byteBMP } } to OLEDsetVideo videoMode { comment 'Switches the entire display: Inverse: inverse video mode (bit 0 = on) Normal: normal video mode (bit 1 = on). Any image on the display will be preserved when mode changes.' '_initCheck' if (videoMode == 'Inverse') { local 'i2cCmd' 'A7' } else { local 'i2cCmd' 'A6' } '_sendCmd' i2cCmd } to OLEDshowGDBuffer { comment 'Copies contents of virtual _GDBuffer to display i2c: in 64 byte chunks for speed. page selection is used to work on OLED with SH1106 cipset spi: fastest is dump buffer _GDBuffer is in decimal' '_initCheck' '_sendCmd' '40' if (_comMode == 'i2c') { local 'idx' 1 for j 8 { '[sensors:i2cWrite]' OLEDi2cAddr ('[data:makeList]' (hexToInt '80') ((hexToInt 'B0') + (j - 1))) '_sendCmd' _ColumnOffset '_sendCmd' '10' '[sensors:i2cWrite]' OLEDi2cAddr ('[data:join]' _dataPrefix ('[data:copyFromTo]' _GDBuffer idx (idx + 63))) idx += 64 '[sensors:i2cWrite]' OLEDi2cAddr ('[data:join]' _dataPrefix ('[data:copyFromTo]' _GDBuffer idx (idx + 63))) idx += 64 } } else { '_SPIWriteData' '[sensors:spiExchange]' ('[data:copyFromTo]' _GDBuffer 1) } _delayGDUpd = (booleanConstant false) } to OLEDwrite string x y invFlag optionalScale { comment 'Writes strings to display at any x,y; processing CR LF and wrapping at col x. _textX and _textY are next write locations.' '_initCheck' local 'oldDelayGDUpd' _delayGDUpd _delayGDUpd = true '_verifyXY' x 0 local 'origX' x local 'scale' (argOrDefault 5 1) if (not (isType string 'string')) { string = ('[data:join]' '' string) } for char string { local 'unicode' ('[data:unicodeAt]' 1 char) if (13 == unicode) { noop } (10 == unicode) { x = origX y += (8 * scale) if (y > 63) { y = 0 } } (and (unicode > 31) (unicode < 128)) { '_OLEDwriteChar' char x y invFlag scale x += (7 * scale) if (x > 127) { x = origX y += (8 * scale) if (y > 63) { y = 0 } } } else { comment 'bad char - STOP' sayIt 'Invalid CHAR value:' char 'uniCode:' unicode stopTask } } _textX = x _textY = y _delayGDUpd = oldDelayGDUpd if (not _delayGDUpd) { OLEDshowGDBuffer } } to OLEDwru { comment 'Next Row and Column to print _textY, _textX' return ('[data:asByteArray]' ('[data:makeList]' _textY _textX)) } to '_GDDRAMoff' { comment 'Displays a full empty screen of reverse video. It disengages the hardware GDBuffer. ' local 'i2cCmd' 'A5' '_sendCmd' i2cCmd } to '_GDDRAMon' { comment 'It disables the GDDRAMoff mode and engages the hardware GDBuffer for display content.' local 'i2cCmd' 'A4' '_sendCmd' i2cCmd } to '_OLEDcursorReset' { comment 'Sets display bounds to full range and resets cursor to the origin top-left (0,0) Rows: 0-7 Cols: 0-127 Cursor position is affected by any display operation and also by OLEDColMode block.' '_initCheck' local 'cmdString' '22,00,07,21,00,7F' '_sendCmd' cmdString _textX = 0 _textY = 0 } to '_OLEDflip' flip { comment 'Flips the display initialization horizontally or vertically. Horizontal or vertical is based on the pin connector location.' if flip { '_sendCmd' 'A0,C0' } else { '_sendCmd' 'A1,C8' } } to '_OLEDreset' pin { comment 'Does a power off and on on the display, thus forcing a hardware initialization.' digitalWriteOp pin false waitMillis 1 digitalWriteOp pin true waitMillis 1 } to '_OLEDsetDisplay' onoff { comment 'Puts the display into Off:SLEEP On:WAKE mode. Images on display are preserved.' '_initCheck' if (onoff == 'On') { local 'i2cCmd' 'AF' } else { local 'i2cCmd' 'AE' } '_sendCmd' i2cCmd } to '_OLEDwriteChar' char x y inverse scale { local 'shape' ('[misc:shapeforChar]' char) if (shape == 0) { return 0 } for row ('[data:range]' 0 ((9 * scale) - 1)) { for col ('[data:range]' 0 ((7 * scale) - 1)) { OLEDpixel (x + col) (y + row) (not inverse) } } x += scale y += scale local 'y0' y for c 5 { local 'mask' 1 y = y0 repeat 8 { repeat scale { repeat scale { if (((at c shape) & mask) != 0) { OLEDpixel x y inverse } else { } x += 1 } x += (0 - scale) y += 1 } mask = (mask << 1) } x += scale } } to '_SPIWriteCmd' { comment 'In SPI mode, we send either a write command or write data control code. Then follow it with the appropriate command/data bundle.' digitalWriteOp _dcPin false } to '_SPIWriteData' { comment 'In SPI mode, we send either a write command or write data control code. Then follow it with the appropriate command/data bundle.' digitalWriteOp _dcPin true } to '_clearDisplay' { comment 'Clear Screen without initializing _GDBuffer' '_initCheck' '_OLEDcursorReset' local 'clearBuff' ('[data:newByteArray]' 128 0) '_sendCmd' '40' for j 8 { '[sensors:i2cWrite]' OLEDi2cAddr ('[data:makeList]' (hexToInt '80') ((hexToInt 'B0') + (j - 1))) '_sendCmd' '01' '_sendCmd' '10' '[sensors:i2cWrite]' OLEDi2cAddr ('[data:join]' _dataPrefix clearBuff) } for j 8 { '[sensors:i2cWrite]' OLEDi2cAddr ('[data:makeList]' (hexToInt '80') ((hexToInt 'B0') + (j - 1))) '_sendCmd' '03' '_sendCmd' '10' '[sensors:i2cWrite]' OLEDi2cAddr ('[data:join]' _dataPrefix clearBuff) } } to '_corner' loc cx cy r erase filled { comment 'Calculates and displays the rounded corners for the rectangles. loc is one of TL, TR, BL, BR ... topLeft, topright, bottomleft, bottomright cx,cy are the corner coordinates for the rounded corner. r is the radius in pixels of the arc to be calculated.' local 'x' (-1 * r) local 'y' 0 local 'err' (2 - (2 * r)) if (loc == 'TL') { cx += r cy += r } (loc == 'TR') { cx += (0 - r) cy += r } (loc == 'BL') { cx += r cy += (0 - r) } (loc == 'BR') { cx += (0 - r) cy += (0 - r) } repeatUntil (x > 0) { if filled { if (loc == 'TL') { OLEDdrawLine (cx + x) (cy - y) cx (cy - y) erase } (loc == 'TR') { OLEDdrawLine cx (cy + x) (cx + y) (cy + x) erase } (loc == 'BL') { OLEDdrawLine (cx - y) (cy - x) cx (cy - x) erase } (loc == 'BR') { OLEDdrawLine cx (cy + y) (cx - x) (cy + y) erase } } else { if (loc == 'TL') { OLEDpixel (cx + x) (cy - y) erase } (loc == 'TR') { OLEDpixel (cx + y) (cy + x) erase } (loc == 'BL') { OLEDpixel (cx - y) (cy - x) erase } (loc == 'BR') { OLEDpixel (cx - x) (cy + y) erase } } r = err if (r <= y) { y += 1 err = (err + ((y * 2) + 1)) } if (or (r > x) (err > y)) { x += 1 err = (err + ((x * 2) + 1)) } } } to '_dec2hex' num { comment 'Fast version w/o inversion. (~115uSecs)' local '_hexTbl' '0123456789ABCDEF' local 'hexNum' '' repeatUntil (num < 0) { local 'temp' ('[data:join]' (at ((num % 16) + 1) _hexTbl) hexNum) hexNum = temp num = (ifExpression ((num / 16) != 0) (num / 16) -1) } comment 'If not half-byte length, pad it.' hexNum = ('[data:join]' ('[data:copyFromTo]' '000' 1 (4 - (size hexNum))) hexNum) return hexNum } to '_initCheck' { comment 'Verifies Library initialization status.' if (not OLEDReady) { sayIt 'Display needs to be initialized before using the library blocks.' stopTask } } to '_initDisplayHW' { comment '*** DO NOT CHANGE THESE WITHOUT UNDERSTANDING THEIR MEANINGS *** *** IT CAN MAKE THE DISPLAY NOT OPERATE CORRECTLY *** Initializes display HW timings OLED INITIALIZATION STRINGS: 1306: pins are on top 1309: pins are on the right A8 3F - Multiplex Ratio 0F-3F (16mux to 64mux) D3 00 - Display Offset (vertical) 00-63 40 - Display Start Line 40-7F (40:0) A0 - Set Segment Remap A0: Remap 0:0, A1: 127:0 [A0] C0 - Scan Direction C0:0-127, C8:127-0 [C0] DA 12 - COM pin hw config 02:double-high 12:normal bits 81 7F - Set contrast 00-FF (00:dimmest, FF:brightest) A4 - A4:display shows GDRAM, A5:display all ON pixels A6 - A6:Normal video, A7:Inverse video D5 F0 - Set display clock HB:OSC freq / LB:divide ratio (00-FF) D9 22 - Set pre-charge period HB:Phase2 / LB:Phase1 (1-15 DCLK) DB 20 - Set Vcomh deselect level 20:1306(~0.77xVcc), 34:1309(~0.78xVcc) 8D 14 - Charge Pump Setting 10:disable CP, 14:enable CP 20 00 - Set HORIZONTAL mode AF - Display ON/OFF AE:off (sleep mode), AF:on' if (_resetPin != '-') { '_OLEDreset' _resetPin } local 'initCmd' 'A8,3F,D3,00,40,A1,C8,DA,12,81,7F,A4,A6,D5,70,D9,22,DB,34,8D,14,20,00,AF' '_sendCmd' initCmd } to '_initLibrary' { comment 'Sets all Library variables and initializes the display hardware. It also initializes the virtual GDBuffer, and clears the display. NOTE: Displays supported: OLED1306 (0.96in) and OLED1309 (2.42"), selected via boolean position. OLED1309 requires the connection of RESET pin to a digital pin. Both displays are 128x64 pixels or 16x8 characters in size. Make sure the character hex tables are \n (LF) terminated. Otherwise, extra lines are added in between and it gets messed up. eg: A: 00,7C,12,11,11,12,7C,00\n Max i2c IO is 64 bytes: buffered writes need to be max that size - 16 x 64' _eol = ('[data:unicodeString]' 10) comment 'Col starts are in HEX' _dataPrefix = ('[data:newByteArray]' 1 (hexToInt '40')) comment 'There is no FF/255 in th cHexTBL. this value is used to build the byteArray.' _cDecTBL = ('[data:newByteArray]' 768 255) _GDBuffer = ('[data:newByteArray]' 1024) _textX = 0 _textY = 0 _delayGDUpd = (booleanConstant false) OLEDReady = (booleanConstant true) '_OLEDsetDisplay' 'On' repeat 2 { setUserLED true waitMillis 50 setUserLED false waitMillis 50 } } to '_process image data' image { comment 'Processes the new hex image data format. Each _imgHex gets added to _imgData in DEC byteArray format. _byteCount keeps track of total bytes processed. _imgWidth and _imgHeight (mod8) are dimensions.' local '_ptr' 1 comment '_imgData is not initialized yet.' if (or (0 == _imgData) (not (isType _imgData 'byte array'))) { _imgWidth = (hexToInt ('[data:copyFromTo]' image 1 2)) _imgHeight = (hexToInt ('[data:copyFromTo]' image 3 4)) image = ('[data:copyFromTo]' image 5) comment 'If _imgHeight is partial byte, adjust to full byte' if (not (0 == (_imgHeight % 8))) { _imgHeight += (8 - (_imgHeight % 8)) } _imgData = ('[data:newByteArray]' ((_imgWidth * _imgHeight) / 8)) } for byte ('[data:range]' 1 (size image) 2) { atPut (_byteCount + _ptr) _imgData (hexToInt ('[data:copyFromTo]' image byte (byte + 1))) _ptr += 1 } _byteCount += (_ptr - 1) } to '_sendCmd' cmdString { comment 'Input is a comma sep. STRING. MAKE SURE ALL INPUT PARAMETERS ARE HEX' local '_cList' ('[data:split]' cmdString ',') local 'cmdPrefix' (hexToInt '80') if ('i2c' == _comMode) { for cmd _cList { '[sensors:i2cWrite]' OLEDi2cAddr ('[data:asByteArray]' ('[data:makeList]' cmdPrefix (hexToInt cmd))) } } else { '_SPIWriteCmd' for cmd _cList { spiSend (hexToInt cmd) } } } to '_verifyXY' x y { if (and (and (x >= 0) (x <= 127)) (and (y >= 0) (y <= 63))) { return 0 } else { sayIt 'x or y value error:' ('[data:unicodeString]' 10) 'x:' x ' y:' y stopTask } } to 'defer display updates' { _delayGDUpd = (booleanConstant true) } module 'Temperature Humidity (DHT11, DHT22)' Input author MicroBlocks version 1 3 tags sensor dht11 dht22 temperature humidity description 'Support for the DHT11 and DHT22 environmental sensors. These sensors provide temperature and humidity readings. ' variables _dht_temperature _dht_humidity _dhtData _dhtLastReadTime data spec 'r' 'temperature_DHT11' 'temperature (Celsius) DHT11 pin _' 'auto' 4 spec 'r' 'humidity_DHT11' 'humidity DHT11 pin _' 'auto' 4 spec 'r' 'temperature_DHT22' 'temperature (Celsius) DHT22 pin _' 'auto' 4 spec 'r' 'humidity_DHT22' 'humidity DHT22 pin _' 'auto' 4 spec ' ' '_dhtReadData' '_dhtReadData pin _' 'auto any' 4 spec 'r' '_dhtChecksumOkay' '_dhtChecksumOkay' 'any' spec ' ' '_dhtUpdate' '_dhtUpdate _ isDHT11 _' 'auto bool any' 4 true spec 'r' '_dhtReady' '_dhtReady' 'any' spec ' ' '_dhtCaptureData' '_dhtCaptureData _' 'num' 4 to '_dhtCaptureData' pin { '[sensors:captureStart]' pin waitMillis 10 local 'pulses' ('[sensors:captureEnd]') local 'pulseCount' (size pulses) if (pulseCount < 80) { return 0 } (pulseCount > 80) { pulses = ('[data:copyFromTo]' pulses (pulseCount - 79)) } data = pulses local 'byte' 0 for i 40 { if ((at ((2 * i) - 1) pulses) > 40) { comment 'Long pulse - appends a "1" bit' byte += 1 } if ((i % 8) == 0) { atPut (i / 8) _dhtData byte byte = 0 } else { byte = (byte << 1) } } } to '_dhtChecksumOkay' { if (not (isType _dhtData 'list')) {return (booleanConstant false)} local 'checksum' 0 for i 4 { checksum += (at i _dhtData) } checksum = (checksum & 255) return (checksum == (at 5 _dhtData)) } to '_dhtReadData' pin { comment 'Create DHT data array the first time' if (_dhtData == 0) { _dhtData = (newList 5) } comment 'fill with 1''s set checksum will be bad if read fails' atPut 'all' _dhtData 1 comment 'Pull pin low for >18msec to request data' digitalWriteOp pin false waitMillis 20 local 'useDHTPrimitive' (booleanConstant true) if useDHTPrimitive { '_dhtCaptureData' pin return 0 } comment 'Read DHT start pulses (H L H L)' waitUntil (digitalReadOp pin) waitUntil (not (digitalReadOp pin)) waitUntil (digitalReadOp pin) waitUntil (not (digitalReadOp pin)) local 'i' 1 local 'byte' 0 local 'bit' 1 comment 'Read 40 bits (5 bytes)' repeat 40 { waitUntil (digitalReadOp pin) local 'start' (microsOp) waitUntil (not (digitalReadOp pin)) if (((microsOp) - start) > 40) { comment 'Long pulse - append a "1" bit' byte += 1 } if (bit == 8) { atPut i _dhtData byte i += 1 byte = 0 bit = 1 } else { byte = (byte << 1) bit += 1 } waitUntil (not (digitalReadOp pin)) } } to '_dhtReady' { local 'elapsed' ((millisOp) - _dhtLastReadTime) return (or (elapsed < 0) (elapsed > 2000)) } to '_dhtUpdate' pin isDHT11 { if ('_dhtReady') { _dht_temperature = 0 _dht_humidity = 0 '_dhtReadData' pin _dhtLastReadTime = (millisOp) } if ('_dhtChecksumOkay') { if isDHT11 { _dht_temperature = (at 3 _dhtData) _dht_humidity = (at 1 _dhtData) } else { local 'n' (((at 1 _dhtData) * 256) + (at 2 _dhtData)) _dht_humidity = ((n + 5) / 10) n = ((((at 3 _dhtData) & 127) * 256) + (at 4 _dhtData)) if (((at 3 _dhtData) & 128) != 0) { n = (0 - n) } _dht_temperature = ((n + 5) / 10) } } } to humidity_DHT11 pin { '_dhtUpdate' pin true return _dht_humidity } to humidity_DHT22 pin { '_dhtUpdate' pin false return _dht_humidity } to temperature_DHT11 pin { '_dhtUpdate' pin true return _dht_temperature } to temperature_DHT22 pin { '_dhtUpdate' pin false return _dht_temperature } module Tone Output author MicroBlocks version 1 10 tags tone sound music audio note speaker choices tone_NoteName 'nt;c' 'nt;c#' 'nt;d' 'nt;d#' 'nt;e' 'nt;f' 'nt;f#' 'nt;g' 'nt;g#' 'nt;a' 'nt;a#' 'nt;b' description 'Audio tone generation. Make music with MicroBlocks! ' variables _tonePin _toneInitalized _toneLoopOverhead _toneNoteNames _toneArezzoNotes _toneFrequencies spec ' ' 'play tone' 'play note _ octave _ for _ ms' 'str.tone_NoteName num num' 'nt;c' 0 500 spec ' ' 'playMIDIKey' 'play midi key _ for _ ms' 'num num' 60 500 spec ' ' 'play frequency' 'play frequency _ for _ ms' 'num num' 261 500 space spec ' ' 'startTone' 'start tone _ Hz' 'num' 440 spec ' ' 'stopTone' 'stop tone' space spec ' ' 'attach buzzer to pin' 'attach buzzer to pin _' 'auto' '' space spec 'r' '_measureLoopOverhead' '_measureLoopOverhead' spec 'r' '_baseFreqForNote' '_baseFreqForNote _' 'auto' 'c' spec 'r' '_baseFreqForSemitone' '_baseFreqForSemitone _' 'num' 0 spec ' ' '_toneLoop' '_toneLoop _ for _ ms' 'num num' 440000 100 spec 'r' '_trimmedLowercase' '_trimmedLowercase _' 'str' 'A. b C...' spec ' ' '_tone init note names' '_tone init note names' to '_baseFreqForNote' note { comment 'Return the frequency for the given note in the middle-C octave scaled by 1000. For example, return 440000 (440Hz) for A. Note names may be upper or lower case. Note names may be followed by # for a sharp or b for a flat.' local 'normalized note' ('_trimmedLowercase' note) 'normalized note' = (ifExpression ((at 1 (v 'normalized note')) == 'n') (v 'normalized note') ('[data:join]' 'nt;' (v 'normalized note'))) '_tone init note names' if (('[data:find]' (v 'normalized note') _toneArezzoNotes) > 0) { return ('_baseFreqForSemitone' ('[data:find]' (v 'normalized note') _toneArezzoNotes)) } else { return ('_baseFreqForSemitone' ('[data:find]' (v 'normalized note') _toneNoteNames)) } } to '_baseFreqForSemitone' semitone { if (_toneFrequencies == 0) {_toneFrequencies = ('[data:makeList]' 261626 277183 293665 311127 329628 349228 369994 391995 415305 440000 466164 493883 246942 277183 277183 311127 311127 349228 329628 369994 369994 415305 415305 466164 466164 523252)} if (and (1 <= semitone) (semitone <= (size _toneFrequencies))) { return (at semitone _toneFrequencies) } else { comment 'Bad note name; return 10 Hz' return 10000 } } to '_measureLoopOverhead' { comment 'Measure the loop overhead on this device' local 'halfCycle' 100 local 'startT' (microsOp) repeat 100 { digitalWriteOp _tonePin false waitMicros halfCycle digitalWriteOp _tonePin false waitMicros halfCycle } local 'usecs' ((microsOp) - startT) return ((usecs - 20000) / 200) } to '_tone init note names' { if (_toneNoteNames == 0) { _toneNoteNames = ('[data:makeList]' 'nt;c' 'nt;c#' 'nt;d' 'nt;d#' 'nt;e' 'nt;f' 'nt;f#' 'nt;g' 'nt;g#' 'nt;a' 'nt;a#' 'nt;b' 'nt;c_' 'nt;db' 'nt;d_' 'nt;eb' 'nt;e_' 'nt;e#' 'nt;f_' 'nt;gb' 'nt;g_' 'nt;ab' 'nt;a_' 'nt;bb' 'nt;b_' 'nt;b#') _toneArezzoNotes = ('[data:makeList]' 'nt;do' 'nt;do#' 'nt;re' 'nt;re#' 'nt;mi' 'nt;fa' 'nt;fa#' 'nt;sol' 'nt;sol#' 'nt;la' 'nt;la#' 'nt;si' 'nt;do_' 'nt;dob' 'nt;re_' 'nt;reb' 'nt;mi_' 'nt;mi#' 'nt;fa_' 'nt;solb' 'nt;sol_' 'nt;lab' 'nt;la_' 'nt;sib' 'nt;si_' 'nt;si#') } } to '_toneLoop' scaledFreq ms { if (_toneInitalized == 0) {'attach buzzer to pin' ''} if ('[io:hasTone]') { '[io:playTone]' _tonePin (scaledFreq / 1000) waitMillis ms '[io:playTone]' _tonePin 0 } else { local 'halfCycle' ((500000000 / scaledFreq) - _toneLoopOverhead) local 'cycles' ((ms * 500) / halfCycle) repeat cycles { digitalWriteOp _tonePin true waitMicros halfCycle digitalWriteOp _tonePin false waitMicros halfCycle } } } to '_trimmedLowercase' s { comment 'Return a copy of the given string without whitespace or periods and all lowercase.' local 'result' (newList (size s)) '[data:delete]' 'all' result for i (size s) { local 'ch' ('[data:unicodeAt]' i s) if (and (ch > 32) (ch != 46)) { if (and (65 <= ch) (ch <= 90)) {ch = (ch + 32)} '[data:addLast]' ch result } } return ('[data:unicodeString]' result) } to 'attach buzzer to pin' pinNumber { if (pinNumber == '') { comment 'Pin number not specified; use default pin for this device' if ((boardType) == 'Citilab ED1') { _tonePin = 26 } ((boardType) == 'M5Stack-Core') { _tonePin = 25 } ((boardType) == 'M5StickC') { _tonePin = 26 } ((boardType) == 'Calliope') { digitalWriteOp 23 true digitalWriteOp 24 true _tonePin = 25 } ((boardType) == 'D1-Mini') { _tonePin = 12 } ((boardType) == 'CodingBox') { _tonePin = 32 } else { _tonePin = -1 } } else { _tonePin = pinNumber } _toneLoopOverhead = ('_measureLoopOverhead') _toneInitalized = (booleanConstant true) } to 'play frequency' freq ms { '_toneLoop' (freq * 1000) ms } to 'play tone' note octave ms { local 'freq' ('_baseFreqForNote' note) if (freq <= 10000) { waitMillis ms return 0 } if (octave < 0) { repeat (absoluteValue octave) { freq = (freq / 2) } } repeat octave { freq = (freq * 2) } '_toneLoop' freq ms } to playMIDIKey key ms { local 'freq' ('_baseFreqForSemitone' ((key % 12) + 1)) local 'octave' ((key / 12) - 5) if (octave < 0) { repeat (absoluteValue octave) { freq = (freq / 2) } } repeat octave { freq = (freq * 2) } '_toneLoop' freq ms } to startTone freq { if (_toneInitalized == 0) {'attach buzzer to pin' ''} if ('[io:hasTone]') {'[io:playTone]' _tonePin freq} } to stopTone { startTone 0 } module WiFi Comm author MicroBlocks version 1 10 tags communication network description 'Connect to a WiFi network. Used in conjunction with other network libraries, such as HTTP client, HTTP server or Web Thing. ' spec ' ' 'wifiConnect' 'wifi connect to _ password _ : IP _ gateway _ subnet _' 'str str auto auto auto' 'Network_Name' '' '192.168.1.42' '192.168.1.1' '255.255.255.0' spec ' ' 'wifiCreateHotspot' 'wifi create hotspot _ password _' 'str str' 'Network_Name' 'Network_Password' spec ' ' '[net:stopWiFi]' 'stop wifi' space spec ' ' '[net:setDomainName]' 'wifi set local domain name _ .local' 'str' 'microblocks' space spec 'r' 'getIPAddress' 'IP address' spec 'r' '[net:myMAC]' 'MAC address' space spec ' ' '[net:allowWiFiAndBLE]' 'allow WiFi while using BLE _' 'bool' true to getIPAddress { return ('[net:myIPAddress]') } to wifiConnect ssid password fixedIP gatewayIP subnetIP { if (not ('[net:hasWiFi]')) {return} '[net:stopWiFi]' if ((pushArgCount) < 5) { '[net:startWiFi]' ssid password } else { '[net:startWiFi]' ssid password false fixedIP gatewayIP subnetIP } local 'startMSecs' (millisOp) repeatUntil (('[net:myIPAddress]') != '0.0.0.0') { comment 'Timeout after N seconds' if (((millisOp) - startMSecs) > 30000) { sayIt 'Could not connect' return 0 } comment 'Slow blink while trying to connect' setUserLED true waitMillis 300 setUserLED false waitMillis 300 } repeat 6 { comment 'Quick blinks when connected' setUserLED true waitMillis 50 setUserLED false waitMillis 50 } '[net:setDomainName]' 'microblocks' sayIt 'My IP address is:' ('[net:myIPAddress]') } to wifiCreateHotspot ssid password { if (not ('[net:hasWiFi]')) {return} if ((size password) < 8) { sayIt 'Password must be at least 8 characters' return 0 } '[net:startWiFi]' ssid password true if ('Connected' != ('[net:wifiStatus]')) { sayIt 'Could not create hotspot' return 0 } repeat 6 { comment 'Quick blinks when connected' setUserLED true waitMillis 50 setUserLED false waitMillis 50 } '[net:setDomainName]' 'microblocks' sayIt 'My IP address is:' ('[net:myIPAddress]') }