module main author unknown version 1 0 description '' variables distance script 47 40 { whenStarted OLEDInit_I2C 'OLED_0.96in' '3C' 0 false OLEDwrite 'Welcome' 0 0 false } script 876 52 { whenBroadcastReceived '4display' comment 'display 1 to 10' tmInitialize 18 19 tmClear tmDisplayNumber 0 for i 10 { tmDisplayNumber i waitMillis 1000 } } script 55 204 { whenCondition (not (digitalReadOp 13 'up')) comment 'Button is Press' forever { sendBroadcast 'distance' waitMillis 500 } } script 876 357 { whenBroadcastReceived 'OLED_Display' OLEDwrite 'Welcome' 0 0 false OLEDwrite 'FabAcademy 2026' 0 20 false OLEDwrite 'Dolphin assignment' 0 40 false } script 53 406 { whenBroadcastReceived 'distance' comment 'OLED display distance' distance = ('distance (cm)' 18 19) sayIt distance graphIt distance OLEDclear OLEDwrite 'distance= ' 0 20 false OLEDwrite distance 0 40 false } script 713 503 { whenBroadcastReceived 'servo' setServoAngle 18 90 waitMillis 1000 setServoAngle 18 0 waitMillis 1000 setServoAngle 18 -90 } script 888 528 (digitalReadOp 13 'up') module 'Distance (HC-SR04)' Input author 'Joan Guillén & Josep Ferràndiz' version 1 4 tags sensor 'hc-sr04' distance ultrasound description 'Support for the HC-SR04 ultrasound distance sensor.' variables _sr04_last spec 'r' 'distance (cm)' 'distance (cm) trigger _ echo _' 'num num' 2 4 spec 'r' 'sr04_distanceOnePin' 'distance (cm) pin _' 'num' 0 to 'distance (cm)' trig ech { comment 'Contributed by Joan Guillén and Josep Ferràndiz' digitalWriteOp trig false waitMicros 2 digitalWriteOp trig true waitMicros 50 digitalWriteOp trig false local 'start' (microsOp) waitUntil (or (not (digitalReadOp ech)) (((microsOp) - start) > 23320)) waitUntil (or (digitalReadOp ech) (((microsOp) - start) > 23320)) if (((microsOp) - start) > 23320) { comment 'Distance sensor not ready; return the last distance reading' return _sr04_last } comment 'Pulse sent. Measure time until echo is detected.' start = (microsOp) waitUntil (or (not (digitalReadOp ech)) (((microsOp) - start) > 23320)) _sr04_last = ((10 * ((microsOp) - start)) / 583) comment 'Leave some time for reverberations to die away.' waitMillis 10 return _sr04_last } to sr04_distanceOnePin pin { return ('distance (cm)' pin pin) } module 'Four Digit Display (TM1637)' Output author MicroBlocks version 1 0 description 'Four digit LED display that can display decimal numbers from -999 to 9999. Datasheet: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/unit/digi_clock/TM1637.pdf' variables _tmClockPin _tmDataPin _tmBrightness _tmShowColon spec ' ' 'tmInitialize' 'four digit initialize clock _ data _' 'num num' 1 2 spec ' ' 'tmDisplayNumber' 'four digit show number _' 'num' 10 spec ' ' 'tmClear' 'four digit clear' space spec ' ' 'tmSetBrightness' 'four digit brightness _ (0 to 7)' 'num' 2 spec ' ' 'tmShowColon' 'four digit show colon _' 'bool' false space spec 'r' '_tmIsInitialized' '_tmIsInitialized' spec 'r' '_tmShowSegments' '_tmShowSegments _ _' 'num num' 255 1 spec 'r' '_tmSegmentsForNum' '_tmSegmentsForNum _' 'num' 10 space spec ' ' '_tmWriteByte' '_tmWriteByte _' 'num' 0 spec ' ' '_tmStart' '_tmStart' spec ' ' '_tmStop' '_tmStop' spec ' ' '_tmBitDelay' '_tmBitDelay' to '_tmBitDelay' { waitMicros 10 } to '_tmIsInitialized' { if (and (_tmClockPin == 0) (_tmDataPin == 0)) { sayIt 'Four digit display is not initialized' waitMillis 500 return (booleanConstant false) } return (booleanConstant true) } to '_tmSegmentsForNum' n { comment 'Return the segment bits for the given hex value (0-15).' if (or (n < 0) (n > 15)) { return 0 } local 'i' ((3 * n) + 1) return (hexToInt ('[data:copyFromTo]' '3f,06,5b,4f,66,6d,7d,07,7f,6f,77,7c,39,5e,79,71' i (i + 1))) } to '_tmShowSegments' value index { comment 'Set the segment bits for the given digit. Index 1 is the right-most digit.' if _tmShowColon { value = (value | 128) } '_tmStart' '_tmWriteByte' (hexToInt '44') '_tmStop' '_tmStart' '_tmWriteByte' ((hexToInt 'C0') | (4 - index)) '_tmWriteByte' value '_tmStop' '_tmStart' '_tmWriteByte' ((hexToInt '88') + _tmBrightness) '_tmStop' } to '_tmStart' { digitalWriteOp _tmClockPin true digitalWriteOp _tmDataPin true digitalWriteOp _tmDataPin false digitalWriteOp _tmClockPin false } to '_tmStop' { digitalWriteOp _tmClockPin false digitalWriteOp _tmDataPin false digitalWriteOp _tmClockPin true digitalWriteOp _tmDataPin true } to '_tmWriteByte' byte { repeat 8 { digitalWriteOp _tmClockPin false if ((byte & 1) == 1) { digitalWriteOp _tmDataPin true } else { digitalWriteOp _tmDataPin false } '_tmBitDelay' digitalWriteOp _tmClockPin true byte = (byte >> 1) '_tmBitDelay' } comment 'Clock ack bit (ignored)' digitalWriteOp _tmClockPin false digitalWriteOp _tmDataPin false digitalWriteOp _tmClockPin true } to tmClear { if (not ('_tmIsInitialized')) {return 0} for i 4 { '_tmShowSegments' 0 i } } to tmDisplayNumber n { if (not ('_tmIsInitialized')) {return 0} if (n == 0) { tmClear '_tmShowSegments' ('_tmSegmentsForNum' 0) 1 return 0 } local 'negative' (n < 0) if negative { n = (minimum (absoluteValue n) 999) } else { n = (minimum n 9999) } local 'i' 1 repeatUntil (n == 0) { '_tmShowSegments' ('_tmSegmentsForNum' (n % 10)) i n = (n / 10) i += 1 } if negative { comment 'leading minus sign' '_tmShowSegments' 64 i i += 1 } repeatUntil (i > 4) { '_tmShowSegments' 0 i i += 1 } } to tmInitialize clockPin dataPin { _tmClockPin = clockPin _tmDataPin = dataPin _tmBrightness = 2 _tmShowColon = false for i 4 { '_tmShowSegments' 0 i } } to tmSetBrightness brightness { _tmBrightness = (maximum 0 (minimum brightness 7)) } to tmShowColon showColon { _tmShowColon = showColon } 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 'OLED Graphics' Output author 'Turgut Guneysu and Miroslav Klimos' version 1 22 choices ModeMenu Horizontal Vertical choices OnOffMenu On Off choices VideoMenu Inverse Normal choices DispTypeMenu 'OLED_0.96in' 'OLED_1.3in' 'OLED_1.54in' 'OLED_2.42in' 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 Servo Output author MicroBlocks version 1 4 tags servo motor angle rotation position description 'Control both positional (angle) and rotational servo motors. ' variables _servoPin _servoPulseWidth spec ' ' 'setServoAngle' 'set servo _ to _ degrees (-90 to 90)' 'num num' 1 90 spec ' ' 'setServoSpeed' 'set servo _ to speed _ (-100 to 100)' 'num num' 1 100 spec ' ' 'stopServo' 'stop servo _' 'num' 1 spec 'r' '_servoIndex' '_servoIndex _' 'num' 1 spec ' ' '_servoPulse' '_servoPulse pin _ usecs _' 'num num' 1 1500 spec ' ' '_servoUpdateLoop' '_servoUpdateLoop' to '_servoIndex' which { if (_servoPin == 0) { _servoPin = ('[data:makeList]') _servoPulseWidth = ('[data:makeList]') sendBroadcast '_servoUpdateLoop' } local 'i' ('[data:find]' which _servoPin) if (i < 0) { comment 'Add new pin' '[data:addLast]' which _servoPin '[data:addLast]' 1500 _servoPulseWidth i = (size _servoPin) } return i } to '_servoPulse' pin usecs { if (usecs == 0) { comment 'Servo stopped; do nothing' return 0 } usecs = (maximum 500 (minimum usecs 2900)) comment 'Split wait into a long wait followed by a wait of <= 30 usecs for greater accuracy' local 'endTime' ((microsOp) + usecs) digitalWriteOp pin true waitMicros (usecs - 30) waitMicros (endTime - (microsOp)) digitalWriteOp pin false } to '_servoUpdateLoop' { forever { if (_servoPin != 0) { comment 'If the _servoPin list is not 0, update the servos' for i (size _servoPin) { local 'pin' (at i _servoPin) local 'usecs' (at i _servoPulseWidth) if (and (pin >= 0) (usecs != 0)) { '_servoPulse' pin usecs } } waitMillis 15 } } } to setServoAngle which degrees optionalReverse { local 'reversed' false if ((pushArgCount) > 2) { reversed = optionalReverse } if reversed { degrees = (0 - degrees) } local 'pulseWidth' (1500 - (10 * degrees)) if ('[io:hasServo]') { '[io:setServo]' which pulseWidth } else { atPut ('_servoIndex' which) _servoPulseWidth pulseWidth } } to setServoSpeed which speed optionalReverse { local 'reversed' false if ((pushArgCount) > 2) { reversed = optionalReverse } if reversed { speed = (0 - speed) } local 'pulseWidth' (1500 - (10 * speed)) if ((absoluteValue speed) < 2) { pulseWidth = 0 } if ('[io:hasServo]') { '[io:setServo]' which pulseWidth } else { atPut ('_servoIndex' which) _servoPulseWidth pulseWidth } } to stopServo which { if ('[io:hasServo]') { '[io:setServo]' which 0 } else { atPut ('_servoIndex' which) _servoPulseWidth 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]') }