howto:thymio:thymio_barcode_reader

Thymio, barcode reader

Requirements:

  • a Thymio II
  • Aseba Studio
  • a micro-SD card (note: while the site does list some conditions, users have reported other cards working despite not conforming to them. take this as you will)
  • an adaptator, if your computer doesn't have a port for micro-SD
  • wav mono sound files under 8000hz, without metadata, encoded “unsigned 8 bit PCM”
  • a printer, or a sheet of paper and a black pen (not every ink works. if your ink reflects too much ir light, Thymio won't be able to recognize it. to avoid this, prefer carbon black ink.)

Name each of your audio files “px”, x being a value between 0 and 32767. Place them at the root of your micro-SD card, and then insert it in Thymio. Plug Thymio to your computer, open Aseba Studio, and open the code. Edit the call sound.play(x) lines under #— execution and replace x with the name of the value you chose for your wav files (example: if your file is named “p16”, write call sound.play(16)). Then, run the code.

Next, you need a sheet of paper with some black lines drawn on. Note that each line and each space between the lines must be minimum 0,5mm and maximum 9cm wide, and minimum 8cm long. Different notes are registered every 0,5mm, and it may read a total of 20 notes. This may be edited in the code, but keep the motor speeds low. If Thymio moves any faster, it can't keep up as well, and gives false values.

#this code reads barcodes based on bar width
	
#speed = 0,5 cm/s ; max 20 notes ; different note every 0,5 cm ; 9 different notes ; min width 0,5 cm ; max width 9cm ; min length 5 cm but 8 recommended ; ink must absorb ir light (carbon black)
#prioritize the bars to be too big rather than too small (for example, if a bar should be 3cm; 3,4cm > 2,9cm). in fact, do make them a millimeter or two wider

These are just specifications, conditions for the code to work as intended and limitations. Disregard “max 40 notes” in the original code, its maximum is 20, I just got myself mixed up.

#-----
var state = 0
var counter = 0
var deque[2+(1*40)]
var n
var note
var pause
timer.period[0]=500 #half a second

Here, I'm setting my variables and the timer.

Technically, deque1) could've just been set by writing var deque[42], but for better legibility, 2 represents the two elements in the deque that serve to count what's inside, and “1*40” means that I can store 40 tuples of 1 element each.

#-----
onevent button.center
if button.center == 1 then
	state = 1
end
onevent button.forward #skip straight to playing sounds if impatient
if button.forward == 1 and state == 1 then
	state = 3
end

If you've downloaded the code, you'll notice this part is actually at the end. It doesn't matter much, though, it's gonna be read the same way if it's before or after, I just thought it'd be more understandable if I put it at the start here.

onevent button.center means that on the event that button.center is updated, or in other words, when it is pressed, then the following plays. In this case, it sets the state variable to 1 when the center button is pressed.

if button.forward == 1 and state == 1, I added this so that it's possible to skip waiting for Thymio to realize that there aren't any more black lines to read.

#-----
onevent timer0
counter++
#--- "white state"
if state == 1 then
call leds.top(0,0,32)
motor.right.target = 25
motor.left.target = 25
	if prox.ground.delta[0] < 700 or prox.ground.delta[1] < 700 then
		call deque.push_back(deque, counter)
		state = 2
		counter = 0
		else if counter > 15 then
			state = 3
		end
	end
end

With the line onevent timer0, I'm telling it to repeat everything that's written underneath every 500ms. Then, we have counter++, which adds 1 to counter every time it reads it : so in this case, that's every 500ms. This is how I count how long something has lasted for.

As stated above, state is equal to 1 when the center button is pressed. if state == 1, now it's going to read this part of the code and ignore everything else that only plays when state is equal to anything but 1. I set the top lights to light up in blue, and both motors to move at a speed of 25 each (which is approximately 1 centimeter per second).

if prox.ground.delta[0] < 700 or prox.ground.delta[1] < 700 then if one or the other ground proximity sensors of Thymio's value2) is under 700, then call deque.push_back(deque, counter). This means that the value of counter, at the time of reading this, is put at the end of the deque. In other words, the time spent from not seeing a black bar to seeing one is placed as an element in the deque so that we can use it later.

state = 2 and counter = 0 sets state to 2 and counter to 0.

else if counter > 15 then if neither of the proximity sensors are under 700 and the value of counter is above 15 (15*500ms, so 7,5 seconds), then we assume that there's no black bars to read down the line, and we set state to 3

#--- "black state"
if state == 2 then
call leds.top(32,0,0)
if prox.ground.delta[0] > 900 or prox.ground.delta[1] > 900 then 
	call deque.push_back(deque, counter) 
	state = 1 
	counter = 0
end
end
#--- transition, check if empty
if state == 3 then
	call sound.play(-1) 
	call leds.top(0,0,0)
	motor.right.target = 0
	motor.left.target = 0
	call deque.size(deque, n) 
		if n == 0 then
			state = 0
		else
			state = 4
			counter = 0
		end
	call deque.pop_front(deque, (pause/2)) 
end

if state == 2 then if state is equal to 2, it lights the leds up in red. Then, it checks if the ground proximity sensors have a value above 900 this time, in which case we assume it means that it's seeing white. We push the value of counter at the end of the deque again and set state back to 1, so that the previous code can be read again, and it can look for more black lines.

So, it's gotta keep repeating those two states until there's no more black lines to read.

After that, we have state 3. As stated above, state is equal to 3 when Thymio hasn't encountered a black line in more than 7,5 seconds. The leds turn off and the motors stop moving.

call sound.play(-1) makes thymio stop playing sound. call deque.size(deque, n) checks how many elements are in the deque, then sets n to that value. if n is equal to 0, that means there's nothing in the deque, and we set state to 0; if there's nothing in the deque there's nothing to play and we can stop requesting anything from Thymio. else if it's not 0, then we set state to 4, and counter to 0.

call deque.pop_front(deque, (pause/2)) pop the first element of deque and set pause to that, then divide it by 2. That first element was the time it took to meet a black line. “pop” means to copy that element and then delete it.

#--- execution
if state == 4 then
		if counter == pause then 
		call deque.pop_front(deque, note) 
			if note <= 1 then 
				call sound.play(11)
				call leds.top(31,1,1)
			end
			if note == 2 then
				call sound.play(12)
				call leds.top(32,5,0)
			end
			if note == 3 then
				call sound.play(13)
				call leds.top(0,32,5)
			end
			if note == 4 then
				call sound.play(14)
				call leds.top(32,0,0)
			end
			if note == 5 then
				call sound.play(15)
				call leds.top(25,5,7)
			end
			if note == 6 then
				call sound.play(16)
				call leds.top(32,0,0)
			end
			if note == 7 then
				call sound.play(17)
				call leds.top(0,32,10)
			end
			if note == 8 then
				call sound.play(18)
				call leds.top(32,32,10)
			end
			if note == 9 then
				call sound.play(19)
				call leds.top(30,10,20)
			end
			if note == 10 then
				call sound.play(110)
				call leds.top(20,10,10)
			end
			if note == 11 then
				call sound.play(111)
				call leds.top(5,20,10)
			end
			if note == 12 then
				call sound.play(112)
				call leds.top(15,10,5)
			end
			if note == 13 then
				call sound.play(113)
				call leds.top(12,0,31)
			end
			if note == 14 then
				call sound.play(114)
				call leds.top(31,28,2)
			end
			if note >= 15 then
				call sound.play(115)
				call leds.top(2,32,4)
			end
		end
		if counter == 1 then
			state = 3
		end
end

if counter == pause then if counter is equal to pause, then call deque.pop_front(deque, note) pop the first element of deque again and set note to that. That element was the time it took Thymio to reach the end of a black bar.

if note == x then, call sound.play(x) play a sound corresponding to each note. The sound is saved on a micro-sd card and inserted in the Thymio.

if counter == 1 then, state = 3 set state to 3 when half a second has passed, so that it can repeat until n is equal to 0 (until there's nothing in the deque anymore)


2)
prox.ground.delta is, directly quoting the official wiki, the difference between reflected light and ambient light, linked to the distance and to the ground colour. As such, if the ink used for your barcode reflects too much light, then it won't be read correctly. I set the value pretty high but it still happens with a lot of ink, which is why I specified in comments at the start of the code that carbon black ink is preferable.
  • howto/thymio/thymio_barcode_reader.txt
  • Dernière modification : 2023/06/27 16:12
  • de marie