Skip to content

Commit

Permalink
Better implementation of the protocol using ThingML
Browse files Browse the repository at this point in the history
  • Loading branch information
ffleurey committed Jan 23, 2017
1 parent 4531255 commit 3095d09
Show file tree
Hide file tree
Showing 6 changed files with 1,655 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Sniff-A-Pillar/Sniff-A-Pillar.ino
Expand Up @@ -234,7 +234,7 @@ void reset_receiver() {
rxbuffer[0] = 0;
}

// Timer0 Overflow
// Timer2 Overflow
ISR(TIMER2_OVF_vect)
{

Expand Down
17 changes: 17 additions & 0 deletions ThingML/.project
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Pillar-Block</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.sintef.thingml.resource.thingml.builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.sintef.thingml.resource.thingml.nature</nature>
</natures>
</projectDescription>
Binary file added ThingML/Capture.dsl
Binary file not shown.
242 changes: 242 additions & 0 deletions ThingML/PillarOneWire.thingml
@@ -0,0 +1,242 @@
import "_Datatypes.thingml"

thing fragment TimerMsgs {
message timer_start(id : UInt8, time : UInt32) @timer_start "true";
message timer_cancel(id : UInt8) @timer_cancel "true";
message timer_timeout(id : UInt8) @timeout "true";
}

thing fragment PillarOneWireMsgs {
message pillar_reset()
message pillar_command(addr : UInt8, cmd0 : UInt8, cmd1 : UInt8, csum : UInt8)
message pillar_command_with_ack(addr : UInt8, cmd0 : UInt8, cmd1 : UInt8, csum : UInt8, ack : UInt8)
message pillar_command_with_response(addr : UInt8, cmd0 : UInt8, cmd1 : UInt8, csum : UInt8, rsp0 : UInt8, rsp1 : UInt8, rsum : UInt8)
message pillar_error();
}

thing PillarOneWireLogger includes PillarOneWireMsgs {

required port onewire {
receives pillar_reset, pillar_command, pillar_command_with_ack, pillar_command_with_response, pillar_error
}

statechart PillarOneWireLoggerSC init Logging {

on entry 'Serial.begin(115200);
Serial.println("READY");'

state Logging {

internal event m:onewire?pillar_reset action 'Serial.println("RESET");'

internal event m:onewire?pillar_command action do
'Serial.print('&m.addr&',HEX);' 'Serial.print(" ");'
'Serial.print('&m.cmd0&',HEX);' 'Serial.print(" ");'
'Serial.print('&m.cmd1&',HEX);' 'Serial.print(" S");'
'Serial.print('&m.csum&',HEX);'
'Serial.println();'
end
internal event m:onewire?pillar_command_with_response action do
'Serial.print('&m.addr&',HEX);' 'Serial.print(" ");'
'Serial.print('&m.cmd0&',HEX);' 'Serial.print(" ");'
'Serial.print('&m.cmd1&',HEX);' 'Serial.print(" S");'
'Serial.print('&m.csum&',HEX);' 'Serial.print(" ");'
'Serial.print('&m.rsp0&',HEX);' 'Serial.print(" ");'
'Serial.print('&m.rsp1&',HEX);' 'Serial.print(" S");'
'Serial.print('&m.rsum&',HEX);'
'Serial.println();'
end
internal event m:onewire?pillar_command_with_ack action do
'Serial.print('&m.addr&',HEX);' 'Serial.print(" ");'
'Serial.print('&m.cmd0&',HEX);' 'Serial.print(" ");'
'Serial.print('&m.cmd1&',HEX);' 'Serial.print(" S");'
'Serial.print('&m.csum&',HEX);' 'Serial.print(" A");'
'Serial.print('&m.ack&',HEX);'
'Serial.println();'
end

internal event m:onewire?pillar_error action 'Serial.println("ERROR");'
}
}
}

thing PillarOneWire includes TimerMsgs, PillarOneWireMsgs
@c_header ""
@c_global "struct PillarOneWire_Instance *_pillar_instance;"
{

required port clock {
receives timer_timeout
sends timer_start, timer_cancel
}

provided port onewire {
sends pillar_reset, pillar_command, pillar_command_with_ack, pillar_command_with_response, pillar_error
}

property rxbuffer : UInt8[6]
property rxindex : UInt8


function timer2_initialize() do
'TCCR2A = 0;' // Normal port operation
'TCCR2B = 0;' // Timer is stopped (set to 2 to start counting)
'TIMSK2 = 7;' // Enable interrupts for overflow, compare A and Compare B
'OCR2A = 7;' // 40 uS when counting from 0 with a /64 prescaling @16MHz
'OCR2B = 62;' // 250 uS'
end

function reset_rxbuffer() do
var i : UInt8 = 0
while (i<6) do
rxbuffer[i] = 0
i = i+1 end
rxindex = 0
end

function timer2_start() 'TCNT2 = 0; TCCR2B = 4;' // start counting with /64 prescaling (=> 4 usec period @16MHz)

function timer2_stop() 'TCCR2B = 0;'

function timer2_interrupt_compare_A() // Store 1 bit in the rx buffer
@c_prototype "ISR(TIMER2_COMPA_vect)"
@c_instance_var_name "_pillar_instance"
do
'PORTD ^= _BV(PD4);' // DEBUG: toggle PD4 when reading the bit (Arduino PIN 4)
if ('PIND & _BV(PD2)') ''&rxbuffer&'['&rxindex / 8&'] |= 1 << (7 - ('&rxindex % 8&'));'
rxindex = rxindex + 1
end

function timer2_interrupt_compare_B()
@c_prototype "ISR(TIMER2_COMPB_vect)"
@c_instance_var_name "_pillar_instance"
do
'PORTB ^= _BV(PB1);'
if (not 'PIND & _BV(PD2)') do // If the bus is low, it is a reset.
timer2_stop()
onewire!pillar_reset()
reset_rxbuffer()
end
end

function timer2_interrupt_overflow() // A complete packet has been received
@c_prototype "ISR(TIMER2_OVF_vect)"
@c_instance_var_name "_pillar_instance"
do
'PORTB ^= _BV(PB0);'
timer2_stop()

if (rxbuffer[0]>16) 'PORTD ^= _BV(PD6);' // ERROR: toggle PD5 when reading the bit (Arduino PIN 5)

if (rxindex == 28) do // It is just a command
onewire!pillar_command(rxbuffer[0], rxbuffer[1], rxbuffer[2], rxbuffer[3]/16)
end
else if (rxindex == 29) do // It is a command with and ack
onewire!pillar_command_with_ack(rxbuffer[0], rxbuffer[1], rxbuffer[2], rxbuffer[3]/16, '(0x08 & '&rxbuffer[3]&') >> 3')
end
else if (rxindex == 48) do // It is a command with a response
onewire!pillar_command_with_response(rxbuffer[0], rxbuffer[1], rxbuffer[2], rxbuffer[3]/16, rxbuffer[3]*16 + rxbuffer[4]/16, rxbuffer[4]*16 + rxbuffer[5]/16, '0x0F & '&rxbuffer[5]&'')
end
else do
'PORTD ^= _BV(PD6);' // DEBUG: toggle PD5 when reading the bit (Arduino PIN 5)
onewire!pillar_error()
end
reset_rxbuffer()
end

function int0_initialize() do
// Setup interrupts in INT0 falling edge (connected to 1 wire bus)
'DDRD &= ~_BV(PD2);' // set PD2 as input
'PORTD &= ~_BV(PD2);' // no pullup
'EICRA = 0x02;' // set INT0 to trigger on falling edge
'EIMSK = 0x01;' // Turns on INT0
end

function int0_interrupt_falling()
@c_prototype "ISR (INT0_vect)"
@c_instance_var_name "_pillar_instance"
do
'PORTD ^= _BV(PD5);' // DEBUG: toggle PD5 when reading the bit (Arduino PIN 5)
if ('TCCR2B > 0 && TCNT2 < 14') do
// Ignore
'PORTD ^= _BV(PD7);' // DEBUG: toggle PD5 when reading the bit (Arduino PIN 5)
end
else do
timer2_stop()
timer2_start()
end
end

function initialize() do
timer2_initialize()
int0_initialize()
reset_rxbuffer()
'// set PB5 led as output and OFF (pin 13)
DDRB |= _BV(PB5); // Output
PORTB &= ~_BV(PB5); // LED OFF

// set PD3 as output
DDRD |= _BV(PD3); // Output
PORTD &= ~_BV(PD3); // LOW

// set PD4 as output (for debug)
DDRD |= _BV(PD4); // Output
PORTD &= ~_BV(PD4); // LOW

// set PD5 as output (for debug)
DDRD |= _BV(PD5); // Output
PORTD &= ~_BV(PD5); // LOW

// set PD6 as output (for debug)
DDRD |= _BV(PD6); // Output
PORTD &= ~_BV(PD6); // LOW

// set PD7 as output (for debug)
DDRD |= _BV(PD7); // Output
PORTD &= ~_BV(PD7); // LOW

// set PB0 as output (for debug)
DDRB |= _BV(PB0); // Output
PORTB &= ~_BV(PB0); // LOW

// set PB1 as output (for debug)
DDRB |= _BV(PB1); // Output
PORTB &= ~_BV(PB1); // LOW

sei();'
end

statechart PillarOneWireSC init Idle {

on entry do
'_pillar_instance = _instance;'
'pinMode(13, OUTPUT);'
initialize()
end

state Idle {
on entry clock!timer_start(0,500)
transition -> Blink event clock?timer_timeout
}

state Blink {
on entry do
'digitalWrite(13, HIGH);'
clock!timer_start(0,250)
end
on exit 'digitalWrite(13, LOW);'
transition -> Idle event clock?timer_timeout
}

}
}

protocol Timer;

configuration PillarOneWireTest {
instance pillar : PillarOneWire
instance logger : PillarOneWireLogger
connector logger.onewire => pillar.onewire
connector pillar.clock over Timer
@hardware_timer "0"
}
93 changes: 93 additions & 0 deletions ThingML/_Datatypes.thingml
@@ -0,0 +1,93 @@
datatype Char<1>
@type_checker "Integer"
@c_type "char"
@c_byte_size "1";

object String
@type_checker "String"
@c_type "char *"
@c_byte_size "2";

datatype Boolean<1>
@type_checker "Boolean"
@c_type "uint8_t"
@c_byte_size "1";

datatype UInt8<1>
@type_checker "Integer"
@c_type "uint8_t"
@c_byte_size "1";

datatype UInt16<2>
@type_checker "Integer"
@c_type "uint16_t"
@c_byte_size "2"
@java_type "int"
@java_primitive "true";

datatype Int8<1>
@type_checker "Integer"
@c_type "int8_t"
@c_byte_size "1";

datatype Int16<2>
@type_checker "Integer"
@c_type "int16_t"
@c_byte_size "2";

datatype UInt32<4>
@type_checker "Integer"
@c_type "uint32_t"
@c_byte_size "4";

datatype Integer<2>
@type_checker "Integer"
@c_type "int16_t"
@c_byte_size "2";


datatype Long<4>
@type_checker "Integer"
@c_type "long"
@c_byte_size "4";

datatype Float<4>
@type_checker "Real"
@c_type "float"
@c_byte_size "4";

enumeration DigitalState
@c_byte_size "1"
@c_type "uint8_t"
{
LOW @enum_val "0"
HIGH @enum_val "1"
}

datatype Byte<1>
@type_checker "Integer"
@c_type "uint8_t"
@c_byte_size "1"
@java_type "byte"
@java_primitive "true"
@scala_type "Byte"
@SenML_type "Double";

enumeration DigitalState
@c_type "uint8_t"
@c_byte_size "1"
{
LOW @enum_val "0"
HIGH @enum_val "1"
}

/*
thing fragment TimerMsgs {
message timer_start(id : UInt8, time : UInt32) @timer_start "true";
message timer_cancel(id : UInt8) @timer_cancel "true";
message timer_timeout(id : UInt8) @timeout "true";
message 25ms_tic() @xms_tic "25";
message 500ms_tic() @xms_tic "500";
}
*/

3 comments on commit 3095d09

@Sickathlete83
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am pretty new to coding, I biggest interest is because I purchased one of these for my kids last Christmas. After a move I am finally getting in play with it. I was hoping editing would be a bit easier. The changes I would like to make are to extend the number of tails that can be added and remove the zig zag from the forward piece, annoys me because it makes the path inconsistent. Any advice would be helpful. Thank you.

@ffleurey
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello! The zigzag and the limitation of the number of tail blocks (16?) are implemented in the firmware of the head so they are not possible to change easily. The only way would probably be to replace the microcontroller in the head (which is a chip-on-board) with a atmega or something like that and re-program it from scratch. The code I have made can be used to manage the tail but that is about it. However, regarding your issues:

  • I believe that the limit of 16 blocks is probably reasonable because with more blocks I think it will be too much weight/friction for the head to pull the whole tail consistently. An upgrade to the traction system would probably be necessary.
  • In my experience the zigzag is not so much of the problem since there is a gyro in the head that is used to keep strait and measure the angles of the turns. Of course it is not perfect but I do not think that removing the zigzag would improve much (the gyro drift is the weak link).

@Sickathlete83
Copy link

@Sickathlete83 Sickathlete83 commented on 3095d09 May 30, 2017 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.