Previously on this blog, I have mentioned that I mentor for the All Saints’ College Robotics club and volunteer on the Robocup Junior West Australian Committee.
Combining my childhood love of Lego with my career as a software developer has provided me with an exciting, fun filled hobby. Using my knowledge to mentor primary and secondary school students and help them find a love of robotics and software development is extremely fulfilling and a driving force behind why I do what I do.
This fifth article covers using the Mindsensors EV3 Sensor Multiplexer with the EV3 Basic extensions for Microsoft Small Basic.
The Mindsensors EV3 Sensor Multiplexer is a Lego Mindstorms compatible device which allows three sensors to be connected to a single sensor port on the controller brick. Using a single EV3 Sensor Multiplexer will allow a robot to have six sensors rather than the usual limit of four.
Note: The latest version of the EV3SensorMux has a casing over the circuit board and so looks a little different to the images above.
This can be extremely useful as a Robocup Junior Australia Open Rescue robot needs to detect the colour of the rescue capsules in the rescue zone. This would require an additional colour sensor.
While it would be possible to create a robot with only four sensors:
- Two Colour Sensors for Line Following
- Ultrasonic Sensor for rescue capsule location and object detection
- Additional Colour Sensor for rescue capsule detection
The ability to connect one or two more sensors can create a more powerful robot, for example:
- Two Colour Sensors for Line Following
- Ultrasonic Sensor for rescue capsule location
- Additional Colour Sensor for rescue capsule detection
- Touch Sensor for object detection
- Optional: Gyro Sensor for improved positional control in rescue zone
Mindsensors provide the drivers (blocks) and samples to use the EV3 Sensor Multiplexer with EV3-G and some other languages (NXC and RobotC), but do not have anything to show how it works with EV3 Basic.
After some initial experimentation and a couple of emails with Reinhard Grafl (the creator of EV3 Basic), I understood enough of how the Sensor.CommunicateI2C() command works with I2C devices to get the EV2 Sensor Multiplexer working.
The following code sample displays the readings from three sensors. It is designed to have the EV3 Sensor Multiplexer plugged into port 4 on the EV3 Brick and have the following sensors connected:
- Channel 1 – Ultrasonic Sensor
- Channel 2 – Colour Sensor
- Channel 3 – Gyro Sensor (currently mounted inverted on my robot)
EV3 Basic – Post EV3 Basic version 1.2
' ** Sensor MUX Test 2 ** ' By David Musgrave ' http://WinthropDC.com ' Last Modified: 22-Aug-2017 ' ** Setup Starts Here ******************************************************************************************************* ' Initialise Variables Finished = "False" Clicks = "" ' Mindsensors.com EV3 Sensor Multiplexer I2CPort = 4 I2CBus = 80 I2CChannel = 0 I2CAddr = 0 I2CReg = 0 I2CWriteByte = 0 I2CWriteData = Vector.Init(2, 0) I2CReadByte = 0 I2CReadData = Vector.Init(2, 0) UltraSonic = 0 Gyro = 0 Light = 0 ' ** Subroutines Starts Here ******************************************************************************************************* Sub InitUltrasonic I2CChannel = 1 I2CAddr = I2CBus + 1 * (I2CChannel - 1) I2CReg = 82 ' 0x52 Sensor Mode Sensor.WriteI2CRegister(I2CPort, I2CAddr, I2CReg, 0) ' Ultrasonic Mode 0 = CM ReadUltrasonic() EndSub Sub ReadUltrasonic ' Out: UltraSonic in cm I2CChannel = 1 I2CAddr = I2CBus + 1 * (I2CChannel - 1) I2CReg = 84 ' 0x54 Sensor Data I2CReadData = Sensor.ReadI2CRegisters(I2CPort, I2CAddr, I2CReg, 2) UltraSonic = (I2CReadData[1] * 256 + I2CReadData[0]) / 10 EndSub Sub InitLight I2CChannel = 2 I2CAddr = I2CBus + 1 * (I2CChannel - 1) I2CReg = 82 ' 0x52 Sensor Mode Sensor.WriteI2CRegister(I2CPort, I2CAddr, I2CReg, 0) ' Color Sensor Mode 0 = Reflected ReadLight() EndSub Sub ReadLight ' Out: Light I2CChannel = 2 I2CAddr = I2CBus + 1 * (I2CChannel - 1) I2CReg = 84 ' 0x54 Sensor Data I2CReadData = Sensor.ReadI2CRegisters(I2CPort, I2CAddr, I2CReg, 2) Light = I2CReadData[1] * 256 + I2CReadData[0] EndSub Sub InitGyro I2CChannel = 3 I2CAddr = I2CBus + 1 * (I2CChannel - 1) I2CReg = 82 ' 0x52 Sensor Mode Sensor.WriteI2CRegister(I2CPort, I2CAddr, I2CReg, 1) ' Gyro Mode 1 = Rate Sensor.WriteI2CRegister(I2CPort, I2CAddr, I2CReg, 0) ' Gyro Mode 0 = Angle ReadGyro() EndSub Sub ReadGyro ' Out: Gyro in Degrees I2CChannel = 3 I2CAddr = I2CBus + 1 * (I2CChannel - 1) I2CReg = 84 ' 0x54 Sensor Data I2CReadData = Sensor.ReadI2CRegisters(I2CPort, I2CAddr, I2CReg, 2) Gyro = I2CReadData[1] * 256 + I2CReadData[0] If Gyro >= 32768 Then Gyro = Gyro - 65536 EndIf Gyro = -Gyro ' Inverted due to mounting position EndSub Sub ResetGyro ReadGyro() While Gyro <> 0 InitGyro() Program.Delay(15) EndWhile EndSub ' ** Main Program Starts Here ******************************************************************************************************* LCD.Clear() LCD.Write( 0*8, 0*10, "SensorMUX Test") LCD.Write( 0*8, 2*10, "Port: " + I2CPort) LCD.Write(10*8, 2*10, "Address: " + I2CBus) InitGyro() InitUltrasonic() InitLight() While Finished = "False" ReadUltrasonic() LCD.Write(0*8, 4*10, "Ultrasonic: "+ Text.GetSubText(" "+(Ultrasonic),Text.GetLength((Ultrasonic)),6) +" cm") ReadGyro() LCD.Write(0*8, 6*10, "Gyro : "+ Text.GetSubText(" "+(Gyro),Text.GetLength((Gyro)),6)) ReadLight() LCD.Write(0*8, 8*10, "Light : "+ Text.GetSubText(" "+(Light),Text.GetLength((Light)),6)) Clicks = Buttons.GetClicks() If Text.IsSubText(Clicks, "E") Then Finished = "True" EndIf EndWhile
Note: The above example uses new Sensor.ReadI2CRegister(), Sensor.ReadI2CRegisters(), Sensor.WriteI2CRegister() and Sensor.WriteI2CRegisters() commands rather than the Sensor.CommunicateI2C() command.
EV3 Basic – Pre EV3 Basic version 1.2
' ** Sensor MUX Test ** ' By David Musgrave ' http://WinthropDC.com ' Last Modified: 26-Jun-2017 ' ** Setup Starts Here ******************************************************************************************************* ' Initialise Variables Finished = "False" Clicks = "" ' Mindsensors.com EV3 Sensor Multiplexer I2CPort = 4 I2CBus = 80 I2CChannel = 0 I2CAddr = 0 I2CReg = 0 I2CWriteByte = 0 I2CWriteData = Vector.Init(2, 0) I2CReadByte = 0 I2CReadData = Vector.Init(2, 0) UltraSonic = 0 Gyro = 0 Light = 0 ' ** Subroutines Starts Here ******************************************************************************************************* Sub InitUltrasonic I2CChannel = 1 I2CAddr = I2CBus + 1 * (I2CChannel - 1) I2CReg = 82 ' 0x52 Sensor Mode I2CWriteByte = 1 + 1 I2CWriteData = Vector.Init(I2CWriteByte, I2CReg) I2CWriteData[1] = 0 ' Ultrasonic Mode 0 = CM I2CReadByte = 1 I2CReadData = Vector.Init(I2CReadByte, 0) I2CReadData = Sensor.CommunicateI2C(I2CPort, I2CAddr, I2CWriteByte, I2CReadByte, I2CWriteData) ReadUltrasonic() EndSub Sub ReadUltrasonic ' Out: UltraSonic in cm I2CChannel = 1 I2CAddr = I2CBus + 1 * (I2CChannel - 1) I2CReg = 84 ' 0x54 Sensor Data I2CWriteByte = 1 + 0 I2CWriteData = Vector.Init(I2CWriteByte, I2CReg) I2CReadByte = 2 I2CReadData = Vector.Init(I2CReadByte, 0) I2CReadData = Sensor.CommunicateI2C(I2CPort, I2CAddr, I2CWriteByte, I2CReadByte, I2CWriteData) UltraSonic = (I2CReadData[1] * 256 + I2CReadData[0]) / 10 EndSub Sub InitLight I2CChannel = 2 I2CAddr = I2CBus + 1 * (I2CChannel - 1) I2CReg = 82 ' 0x52 Sensor Mode I2CWriteByte = 1 + 1 I2CWriteData = Vector.Init(I2CWriteByte, I2CReg) I2CWriteData[1] = 0 ' Color Sensor Mode 0 = Reflected I2CReadByte = 1 I2CReadData = Vector.Init(I2CReadByte, 0) I2CReadData = Sensor.CommunicateI2C(I2CPort, I2CAddr, I2CWriteByte, I2CReadByte, I2CWriteData) ReadLight() EndSub Sub ReadLight ' Out: Light I2CChannel = 2 I2CAddr = I2CBus + 1 * (I2CChannel - 1) I2CReg = 84 ' 0x54 Sensor Data I2CWriteByte = 1 + 0 I2CWriteData = Vector.Init(I2CWriteByte, I2CReg) I2CReadByte = 2 I2CReadData = Vector.Init(I2CReadByte, 0) I2CReadData = Sensor.CommunicateI2C(I2CPort, I2CAddr, I2CWriteByte, I2CReadByte, I2CWriteData) Light = I2CReadData[1] * 256 + I2CReadData[0] EndSub Sub InitGyro I2CChannel = 3 I2CAddr = I2CBus + 1 * (I2CChannel - 1) I2CReg = 82 ' 0x52 Sensor Mode I2CWriteByte = 1 + 1 I2CWriteData = Vector.Init(I2CWriteByte, I2CReg) I2CWriteData[1] = 1 ' Gyro Mode 1 = Rate I2CReadByte = 1 I2CReadData = Vector.Init(I2CReadByte, 0) I2CReadData = Sensor.CommunicateI2C(I2CPort, I2CAddr, I2CWriteByte, I2CReadByte, I2CWriteData) I2CWriteData[1] = 0 ' Gyro Mode 0 = Angle I2CReadData = Sensor.CommunicateI2C(I2CPort, I2CAddr, I2CWriteByte, I2CReadByte, I2CWriteData) ReadGyro() EndSub Sub ReadGyro ' Out: Gyro in Degrees I2CChannel = 3 I2CAddr = I2CBus + 1 * (I2CChannel - 1) I2CReg = 84 ' 0x54 Sensor Data I2CWriteByte = 1 + 0 I2CWriteData = Vector.Init(I2CWriteByte, I2CReg) I2CReadByte = 2 I2CReadData = Vector.Init(I2CReadByte, 0) I2CReadData = Sensor.CommunicateI2C(I2CPort, I2CAddr, I2CWriteByte, I2CReadByte, I2CWriteData) Gyro = I2CReadData[1] * 256 + I2CReadData[0] If Gyro >= 32768 Then Gyro = Gyro - 65536 EndIf Gyro = -Gyro ' Inverted due to mounting position EndSub Sub ResetGyro ReadGyro() While Gyro <> 0 InitGyro() Program.Delay(15) EndWhile EndSub ' ** Main Program Starts Here ******************************************************************************************************* LCD.Clear() LCD.Write( 0*8, 0*10, "SensorMUX Test") LCD.Write( 0*8, 2*10, "Port: " + I2CPort) LCD.Write(10*8, 2*10, "Address: " + I2CBus) InitGyro() InitUltrasonic() InitLight() While Finished = "False" ReadUltrasonic() LCD.Write(0*8, 4*10, "Ultrasonic: "+ Text.GetSubText(" "+(Ultrasonic),Text.GetLength((Ultrasonic)),6) +" cm") ReadGyro() LCD.Write(0*8, 6*10, "Gyro : "+ Text.GetSubText(" "+(Gyro),Text.GetLength((Gyro)),6)) ReadLight() LCD.Write(0*8, 8*10, "Light : "+ Text.GetSubText(" "+(Light),Text.GetLength((Light)),6)) Clicks = Buttons.GetClicks() If Text.IsSubText(Clicks, "E") Then Finished = "True" EndIf EndWhile
Conclusions
For the conclusion, I thought I would try to explain how the Sensor.CommunicateI2C() works with the I2C protocol. The syntax is as follows:
ReadData = Sensor.CommunicateI2C(Port, Address, WriteByte, ReadByte, WriteData)
The values are as follows:
ReadData
This is an array of bytes (values 0 to 255) returned. If the data returned is more than one byte you might need to combine the values together as a High byte and Low byte. It is best practice to initialise the array to zeros before making the call.
Port
This is the port of the EV3 Controller Brick that the I2C device is plugged into.
Address
This is the unique address (0 to 127) for the I2C slave device on the I2C bus. Note: For EV3 Basic this is expressed in Words (16 bit increments). Each device has a default address that it comes with. It is easiest to use the default address even though there is a way to change it. The documentation usually provides this address in hexadecimal in a range of 0x00 to 0xFF (bytes – 8 bit increments). For example: The documentation shows that Channel 1 of the EV3SensorMux is at 0xA0. This is 160 in 8 bit decimal, so you must divide by 2 for the address in 16 bit decimal = 80. Channels 2 and 3 are located at 0xA2 and 0xA4, which converts to 81 and 82 respectively.
WriteByte
The number of bytes of data to send to the device. You can send up to 31 bytes of user data. However, the first byte is the address of the first register to write data to or read data from.
ReadByte
The number of bytes of data to read from the device. At least one byte must be returned, even if you do not use it.
WriteData
This is an array of bytes to send to the device with the first byte being the address of the first register.
So to write a single byte to register 0x52 (82) of value X, your settings should be:
WriteByte = 2, WriteData[0] = 82, WriteData[1] = X, ReadByte = 1, ReadData[0] = 0
Or to read two bytes from register 0x54 (84), your settings should be:
WriteByte = 1, WriteData[0] = 84, ReadByte = 2, ReadData[0] = 0, ReadData[1] = 0
I hope this makes it easier to understand the code example above. Once I worked out that you needed to halve the device address after converting it to decimal, everything started to work. Please refer to the documentation published by Mindsensors for further information on using the EV3 Sensor Multiplexer.
For more information on robotics and the EV3 Basic extensions to Microsoft Small Basic, check out the following links:
In the next article, we will discuss how to use the Mindsensors Motor Multiplexer with EV3 Basic.
David
01-Aug-2022: Updated article with newer code example using new commands for communicating with I2C devices.
This article was originally posted on http://www.winthropdc.com/blog.
is IT also possible to use the colorsensor as rgb sensor with the multiplexer? If i go on Mode 4 the sensor always turns on and of for one second
LikeLike
Changing modes on the EV3 colour sensor always has a delay. Even without a multiplexer swapping between modes takes time. Recently I built a robot that needed both brightness and colour modes and having two sensors without swapping modes allowed the main program loop to run significantly faster than one sensor continuously swapping modes.
As long as the sensor stays working in mode 4 (RGB mode) you can read the Red or Green values for brightness and use the RGB values to calculate colours. No need to swap modes. Note: RGB mode allows the EV3 sensor to “see” greens other than Lego Green.
Hope this helps. David.
LikeLike
I think you understood me wrong. The sensor did Not go to the right Mode. I only had a program to mesure the colors in rgb Mode, but the sensor did Not go to this Mode, he only blinks.
LikeLike
I don’t think I have tried using RGB mode 4 via the multiplexer. It might not work as it needs to return 3 values rather than 1 for the other modes.
Maybe you need to swap with another sensor so you can attach it to the EV3 directly.
LikeLike
Thank you for reply. We Start at robocup junior and need 4 color Sensors. We tried to use the color Mode, but the sensor detect Green where is no Green only example a Mixture of White and Black. With rgb IT works much more better
LikeLike