How to custom the i2c pins and UART2 for a new ESP32 board

Issue #419 resolved
Tom Ming created an issue

@John Maloney Hi John,
I would like to add a new vm for a new board which is popular in China mainland.
It is built on ESP32-Wroom-32 with 8M flash.

- ESP-32 master
  Processor: Tensilica LX6 dual-core processor (one core for high-speed connection; one core for independent application development)
Main frequency: up to 240MHz clock frequency
  SRAM: 520KB
  Flash: 8MB
  Wi-Fi standard: FCC/CE/TELEC/KCC
  Wi-Fi protocol: 802.11 b/g/n/d/e/i/k/r (802.11n, speed up to 150 Mbps), A-MPDU and A-MSDU aggregation, support 0.4us guard interval
  Frequency range: 2.4~2.5GHz
  Bluetooth protocol: Compliant with Bluetooth v4.2 BR/EDR and BLE standards
  Bluetooth audio: CVSD and SBC audio Low power consumption: 10uA
- Power supply mode: Micro USB power supply
- Working voltage: 3.3V
- Maximum working current: 200mA
- Maximum load current: 1000mA
- Control onboard
  Three-axis accelerometer MSA300, measuring range: ±2/4/8/16G
  Geomagnetic sensor MMC5983MA, measurement range: ±8 Gauss; accuracy 0.4mGs, electronic compass error ±0.5°
  light sensor
  microphone
  3 full-color ws2812 lamp beads
  1.3-inch OLED display, supports 16*16 character display, resolution 128x64
  passive buzzer
  Support 2 physical buttons (A/B), 6 touch buttons
  Support 1-way alligator clip interface, which can be easily connected to various resistive sensors
- Expansion interface
  20-channel digital I/O, (including 12-channel PWM, 6-channel touch input)
  5-channel 12bit analog input ADC, P0~P4
  1 way external input alligator clip interface: EXT/GND
  Support I2C, UART, SPI communication protocol

Here is the schematic and hardware.

I have tested it with ESP32 vm,it can work.
But the I2C is custom,it use gpio 23 for SDA and gpio 22 for SCL with built in OLED.

How clould I custom the I2C with a new vm for the board ?And how could I custom the serial2 port for the board?

Thanks.

Comments (18)

  1. John Maloney repo owner

    Hi, Tom.

    That board is supported by PlatformIO so you can get started by adding these lines:

    [env:mpython]
    platform = espressif32
    board = labplus_mpython
    board_build.partitions = noota_3g.csv
    lib_deps =
        WebSockets
        256dpi/MQTT
    

    to the platformio.ini file. Then build and install:

    pio run -e mpython -t upload
    

    The I2C pins should be correct; they are provided by PlatformIO's board definition.

    This should give you a working MicroBlocks. You may want to write a small library for that board to access any special features (e.g. light sensor and microphone).

    If this works for you, I can add the mpython entry to the platformio.ini file in the next pilot release.

  2. Tom Ming reporter

    @John Maloney

    Thanks for your quick reply.

    I could built the vm for labplus_mpython board according to your tips.

    It works,but I can not access the OLED, Geomagnetic sensor,Three-axis accelerometer MSA300 now.

    I would like to try to write libarys for the board to access any special features.

  3. John Maloney repo owner

    If the OLED has an SSD1306 controller, the OLED Graphics library might work.

    I assume the geomagnetic sensor and three-axis accelerometer MSA300 are I2C sensors. By reading their datasheets you should be able to create a libraries for them using the I2C blocks in the Comm category. If you do create one or more libraries, you are welcome to submit for inclusion in MicroBlocks.

    Good luck!

  4. Tom Ming reporter

    @John Maloney

    I noted that the board use 1.3 inch OLED, the datasheet is here.

    I think it may be slightly different from the 0.96 inch oled. That is why I can not access it with MicroBlocks OLED block.

  5. John Maloney repo owner

    I see. that board uses a different OLED controller chip (SH1106 vs. SSD1306).

    From a quick look at the SH1106Wire.h, the SH1106 may not be too different from the SSD1306. By reading the datasheet for the SH1106, it might not take too much work to modify the OLED Graphics library that works with that display.

    Another option would be to build TFT support for that display into the virtual machine. The easiest way to do that would be to use a driver that works with the Adafruit GFX graphics library, such as this one. However, I did a quick test and that library fails to compile due to a reference to an AVR header file. That might be easy to fix -- or not.

    Unfortunately, it looks like either path will require some work.

  6. John Maloney repo owner

    If you want to experiment with SH1106 in MicroBlocks, the code below may be of interest. It is a project that builds up graphics operations in a step-by-step way for the SSD1306, starting with drawing individual pixels. Just copy the code into a file with a .ubp extension and open it in MicroBlocks. You will probably need to change the code in showBuffer to work with SH1106 but once you've gotten that working, everything else should work. Have fun! :-)

    module main
    author unknown
    version 1 0 
    description ''
    variables oledBuffer penX penY penDirection sprites 
    
        spec ' ' 'OLEDInit' 'OLEDInit'
        spec ' ' 'OLEDSendCmd' 'OLEDSendCmd _' 'str' ''
        spec ' ' 'clearBuffer' 'clearBuffer'
        spec ' ' 'showBuffer' 'showBuffer'
        spec ' ' 'drawPixel' 'drawPixel x _ y _ white _' 'num num bool' 0 0 true
        spec ' ' 'drawLine' 'draw line from _ _ to _ _ white _' 'num num num num bool' 0 0 127 63 true
        spec ' ' 'drawCircle' 'draw circle center _ _ radius _ white _' 'num num num bool' 64 32 10 true
        spec ' ' 'home' 'home'
        spec ' ' 'move' 'move _ pen down _' 'num bool' 10 true
        spec ' ' 'goTo' 'go to x _ y _ pen down _' 'num num bool' 30 30 true
        spec ' ' 'turnBy' 'turn _ degrees' 'num' 90
        spec ' ' 'setDirection' 'point in direction _' 'num' 0
        spec ' ' 'addSprite' 'addSprite'
        spec ' ' 'animateSprites' 'animateSprites'
        spec ' ' 'moveSprite' 'moveSprite _' 'num' 10
    
    to OLEDInit {
      clearBuffer
    }
    
    to OLEDSendCmd cmdString {
      comment 'Input is a comma separated string of hex values.'
      for cmd ('[data:split]' cmdString ',') {
        '[sensors:i2cWrite]' 60 ('[data:makeList]' (hexToInt '80') (hexToInt cmd))
      }
    }
    
    to addSprite {
      if (sprites == 0) {sprites = ('[data:makeList]')}
      local 'dx' (random -10 10)
      local 'dy' (random -10 10)
      if (dx == 0) {
        dx = 1
      }
      if (dy == 0) {
        dy = 1
      }
      local 'newSprite' ('[data:makeList]' (random 0 127) (random 0 63) dx dy)
      '[data:addLast]' newSprite sprites
    }
    
    to animateSprites {
      if (sprites == 0) {sprites = ('[data:makeList]')}
      oledBuffer = ('[data:newByteArray]' 1024)
      for sprite sprites {
        moveSprite sprite
        drawCircle (at 1 sprite) (at 2 sprite) 3 true
      }
      showBuffer
    }
    
    to clearBuffer {
      if (0 == oledBuffer) {
        oledBuffer = ('[data:newByteArray]' 1024)
      } else {
        atPut 'all' oledBuffer 0
      }
      showBuffer
    }
    
    to drawCircle cx cy r pixOn {
      comment 'Draw a circle with radius r centered at cx,cy using Bresenham circle algorithm.'
      local 'x' (0 - r)
      local 'y' 0
      local 'err' (2 - (2 * r))
      repeatUntil (x >= 0) {
        drawPixel (cx - x) (cy + y) pixOn
        drawPixel (cx - y) (cy - x) pixOn
        drawPixel (cx + x) (cy - y) pixOn
        drawPixel (cx + y) (cy + x) pixOn
        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 drawLine x0 y0 x1 y1 pixOn {
      comment 'Draws a line from x0,y0 to x1,y1 using the Bresenham Algorithm'
      if (0 == oledBuffer) {
        clearBuffer
      }
      local 'dx' (absoluteValue (x1 - x0))
      local 'dy' (-1 * (absoluteValue (y1 - y0)))
      local 'err' (dx + dy)
      if (x0 < x1) {
        local 'sx' 1
      } else {
        local 'sx' -1
      }
      if (y0 < y1) {
        local 'sy' 1
      } else {
        local 'sy' -1
      }
      forever {
        drawPixel x0 y0 pixOn
        if (and (x0 == x1) (y0 == y1)) {
          return
        }
        local 'e2' (2 * err)
        if (e2 >= dy) {
          err += dy
          x0 += sx
        }
        if (e2 <= dx) {
          err += dx
          y0 += sy
        }
      }
    }
    
    to drawPixel x y pixOn {
      comment 'Set the value of pixel at x,y in GDBuffer.
    Use showBuffer to display it.
    
    Ranges:
      x: 0-127
      y: 0-63
      byteIndex: 1-1024
      bitIndex: 0-7
    '
      comment 'Check ranges'
      if (or (x < 0) (x > 127)) {
        return
      }
      if (or (y < 0) (y > 63)) {
        return
      }
      local 'byteIndex' ((x + ((y / 8) * 128)) + 1)
      local 'bitIndex' (y % 8)
      local 'byte' (at byteIndex oledBuffer)
      if pixOn {
        byte = (byte | (1 << bitIndex))
      } else {
        byte = (byte & ('~' (1 << bitIndex)))
      }
      atPut byteIndex oledBuffer byte
    }
    
    to goTo x y drawFlag {
      local 'startX' (penX >> 14)
      local 'startY' (penY >> 14)
      penX = (x << 14)
      penY = (y << 14)
      if drawFlag {
        drawLine startX startY x y true
      }
    }
    
    to home {
      goTo 64 32 'none'
      setDirection 0
    }
    
    to move n drawFlag {
      comment 'Representation:
      penDirection is in hundreths of a degree (e.g. 4500 means 45 degrees)
      penX and penY are scaled by 16384 (2^14)
    
    The sine function takes an angle in hundreds of a degree and returns a
    number scaled by 16384. penX and penY are also scaled by 16384.
    Those numbers are shifted right by 14 bits to get pixel locations.'
      local 'startX' (penX >> 14)
      local 'startY' (penY >> 14)
      comment 'The cosine is just the sine shifted by 90 degrees.'
      penX += (n * ('[misc:sin]' (penDirection + 9000)))
      penY += (n * ('[misc:sin]' penDirection))
      local 'endX' (penX >> 14)
      local 'endY' (penY >> 14)
      if drawFlag {
        drawLine startX startY endX endY true
      }
    }
    
    to moveSprite sprite {
      local 'x' (at 1 sprite)
      local 'y' (at 2 sprite)
      local 'dx' (at 3 sprite)
      local 'dy' (at 4 sprite)
      comment 'If touching left or right edge, make dx go towards center'
      if (x < 0) {
        dx = (absoluteValue dx)
      }
      if (x > 128) {
        dx = (0 - (absoluteValue dx))
      }
      comment 'If touching top or bottom edge, make dy go towards center'
      if (y < 0) {
        dy = (absoluteValue dy)
      }
      if (y > 64) {
        dy = (0 - (absoluteValue dy))
      }
      comment 'Update sprite position'
      x += dx
      y += dy
      atPut 1 sprite x
      atPut 2 sprite y
      atPut 3 sprite dx
      atPut 4 sprite dy
    }
    
    to setDirection a {
      penDirection = ((a * 100) % 36000)
    }
    
    to showBuffer {
      comment 'Send oledBuffer data to the display.
    Sends data in 32 byte chunks for speed.
    Each chunk starts with the command 0x40.'
      if (0 == oledBuffer) {
        clearBuffer
      }
      comment 'Horizontal mode.'
      OLEDSendCmd '20,00'
      comment 'Set the display buffer write destination to top-left.'
      OLEDSendCmd '22,00,07,21,00,7F'
      local 'buffer' ('[data:newByteArray]' 33)
      atPut 1 buffer (hexToInt '40')
      local 'start' 0
      repeat 32 {
        for i 32 {
          atPut (i + 1) buffer (at (start + i) oledBuffer)
        }
        '[sensors:i2cWrite]' 60 buffer
        start += 32
      }
    }
    
    to turnBy a {
      penDirection += (a * 100)
      penDirection = (penDirection % 36000)
    }
    
    script 83 58 {
    comment '1. Hardware'
    comment '2. Display buffer'
    comment '3: Pixels'
    comment '4. Lines and circles'
    comment '5. Turtle graphics'
    comment '6. Animation'
    }
    
    script 103 482 {
    comment '1. Hardware'
    }
    
    script 134 522 {
    OLEDInit
    }
    
    script 221 523 {
    OLEDSendCmd 'A5'
    }
    
    script 358 586 {
    comment 'normal mode'
    OLEDInit
    OLEDSendCmd 'A4'
    }
    
    script 179 589 {
    comment 'all pixels on'
    OLEDInit
    OLEDSendCmd 'A5'
    }
    
    script 103 915 {
    comment '2. Display buffer'
    }
    
    script 224 946 {
    showBuffer
    }
    
    script 319 946 (v oledBuffer)
    
    script 134 947 {
    clearBuffer
    }
    
    script 196 1019 {
    OLEDInit
    clearBuffer
    for i 128 {
      atPut i oledBuffer 255
    }
    showBuffer
    }
    
    script 109 1408 {
    comment '3: Pixels'
    }
    
    script 148 1443 {
    drawPixel 0 0 true
    }
    
    script 193 1502 {
    OLEDInit
    drawPixel 0 0 true
    showBuffer
    }
    
    script 194 1602 {
    OLEDInit
    clearBuffer
    for i 64 {
      drawPixel i 0 true
    }
    showBuffer
    }
    
    script 128 1866 {
    comment '4. Lines and circles'
    }
    
    script 149 1899 {
    drawLine 0 0 127 63 true
    }
    
    script 342 1900 {
    drawCircle 64 32 10 true
    }
    
    script 174 1956 {
    OLEDInit
    clearBuffer
    drawLine 0 0 127 63 true
    drawCircle 64 32 30 true
    showBuffer
    }
    
    script 343 2073 {
    OLEDInit
    clearBuffer
    for i 30 {
      drawCircle 63 32 (2 * i) true
      showBuffer
    }
    }
    
    script 175 2167 {
    OLEDInit
    clearBuffer
    repeat 100 {
      drawLine (random 0 127) (random 0 63) (random 0 127) (random 0 63) true
      showBuffer
    }
    }
    
    script 73 2393 {
    comment '5. Turtle graphics'
    }
    
    script 159 2428 {
    move 10 true
    }
    
    script 333 2428 {
    goTo 30 30 true
    }
    
    script 98 2429 {
    home
    }
    
    script 261 2456 {
    setDirection 0
    }
    
    script 133 2457 {
    turnBy 90
    }
    
    script 190 2486 (v penY)
    
    script 134 2487 (v penX)
    
    script 249 2487 (v penDirection)
    
    script 179 2542 {
    OLEDInit
    clearBuffer
    home
    repeat 24 {
      repeat 4 {
        move 20 true
        turnBy 90
      }
      turnBy 15
      showBuffer
    }
    }
    
    script 58 2847 {
    comment '6. Animation'
    }
    
    script 90 2882 {
    animateSprites
    }
    
    script 208 2882 {
    moveSprite 10
    }
    
    script 326 2882 {
    addSprite
    }
    
    script 92 2913 (v sprites)
    
    script 197 2933 {
    comment 'Start animation'
    OLEDInit
    repeat 10 {
      addSprite
    }
    clearBuffer
    forever {
      animateSprites
    }
    }
    
    script 387 2972 {
    comment 'Add ten more sprites'
    repeat 10 {
      addSprite
    }
    }
    
    script 365 3152 {
    comment 'A sprite is represenented by four numbers'
    return (at 1 sprites)
    }
    
    script 102 3154 {
    comment 'Report number of sprites'
    return (size sprites)
    }
    
  7. Tom Ming reporter

    @John Maloney

    Hi John,

    I read the OLED datasheet of SSD1306 and sh1106.

    I note that they use different addressing modes.

    It has three addressing mode for SSD1306 OLED, they are Page addressing mode,Horizontal addressing mode and Vertical addressing mode.While SH1106 OLED uses Page addressing.

    I know that OLED Graphics of MircroBlocks use Horizontal addressing mode.

    Therefore I should change the addressing mode for SH1106 OLED, and I should change the code in showBuffer block to work with SH1106 which you gave me above?

    Also, what is the SRAM of the SH1106 OLED? Is it 132 X 64 bit SRAM? Should I have to consider this factor when changing the code in showBuffer block?

    Thanks.

  8. John Maloney repo owner

    I would start by setting one byte of the buffer to 255, then writing the buffer to the SH1106. Once that works, you'll be able to study the datasheet to understand the pixel layout. I think you will only to change the code in "drawPixel" to make it for the SH1106 layout. Once you have that, you will also have lines and circles.

    Also, what is the SRAM of the SH1106 OLED? Is it 132 X 64 bit SRAM? Should I have to consider this factor when changing the code in showBuffer block?

    I'm not sure why they have more RAM than pixels. I'm not sure if you need to consider this. I would experiment by getting "showBuffer" working.

    The datasheet is complicated, but working with these displays is fun once you get something on the screen.

    Good luck!

  9. John Maloney repo owner

    Did you have success with this project? I am marking this resolved since the original question was answered, but feel free to continue to comment here about writing a MicroBlocks library for the SH1106.

  10. John Maloney repo owner

    That's great! Congratulations to Yue Shao (and you) for solving a complex problem. Thanks for letting me know.

  11. Log in to comment