Skip to content

Instantly share code, notes, and snippets.

@dsferruzza
Created July 9, 2015 16:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dsferruzza/955bf8075256b114dcc8 to your computer and use it in GitHub Desktop.
Save dsferruzza/955bf8075256b114dcc8 to your computer and use it in GitHub Desktop.
Arduino Internal DSL in Scala, following the lecture by @petitroll at EJCP2015
case class ArduinoML(transitions: Seq[Transition]) {
def genCode: String = {
val statePrefix = "state_"
val init = transitions.head.from
val bricks = transitions.foldLeft(Set.empty[Brick])((acc, cur) => acc ++ cur.from.actuators ++ cur.to.actuators ++ cur.cond.sensors)
val br = bricks
.map(" " + _.genCode)
.mkString("\n")
val tr = transitions
.map { transition =>
val from = transition.from
val to = transition.to
val actions = from.genActionCode
val cond = transition.cond.genCondCode
s"""
void $statePrefix${from.name}() {
$actions
if ($cond) { $statePrefix${to.name}(); } else { $statePrefix${from.name}(); }
}
"""
}
.mkString("\n")
s"""
// Code generated by ArduinoML
void setup() {
// Here comes brick declaration
$br
}
void loop() {
$statePrefix${init.name}();
}
$tr
"""
}
}
sealed trait Brick {
def pin: Int
def genCode: String = {
val direction = this match {
case a: Actuator => "OUTPUT"
case s: Sensor => "INPUT"
}
s"pinMode($pin, $direction);"
}
}
case class Actuator(pin: Int) extends Brick
case class Sensor(pin: Int) extends Brick with Condition {
def genCondCode: String = s"digitalRead($pin) == HIGH"
def sensors: Set[Sensor] = Set(this)
}
//------------------------------------------------------------------
case class State(n: Symbol, actions: (Actuator, Boolean)*) {
def name: String = n.name
lazy val actuators: Set[Actuator] = actions.map(_._1).toSet
def genActionState(s: Boolean): String = s match {
case true => "HIGH"
case false => "LOW"
}
def genActionCode: String = {
actions
.map {
case (Actuator(pin), state) => s" digitalWrite($pin, ${genActionState(state)});"
}
.mkString("\n")
}
}
case class Transition(s: (State, State), cond: Condition) {
lazy val from: State = s._1
lazy val to: State = s._2
}
sealed trait Condition {
def genCondCode: String
def sensors: Set[Sensor]
}
case class Not(c: Condition) extends Condition {
def genCondCode: String = c match {
case Sensor(pin) => s"digitalRead($pin) == LOW"
case cond@And(_) => Or(cond.c.map(con => Not(con)): _*).genCondCode
case cond@Or(_) => And(cond.c.map(con => Not(con)): _*).genCondCode
case Not(c) => c.genCondCode
case cond@_ => s"!(${cond.genCondCode})"
}
def sensors: Set[Sensor] = c.sensors
}
case class And(c: Condition*) extends Condition {
def genCondCode: String = {
val conds = c.map(_.genCondCode).mkString(" && ")
s"($conds)"
}
def sensors: Set[Sensor] = c.flatMap(_.sensors).toSet
}
case class Or(c: Condition*) extends Condition {
def genCondCode: String = {
val conds = c.map(_.genCondCode).mkString(" || ")
s"($conds)"
}
def sensors: Set[Sensor] = c.flatMap(_.sensors).toSet
}
object Hello extends App {
val light = Actuator(12)
val button = Sensor(9)
val button2 = Sensor(8)
val stateOn = State('light_on, light -> true)
val stateOn2 = State('light_on2, light -> true)
val stateOff = State('light_off, light -> false)
val stateOff2 = State('light_off2, light -> false)
val t1 = And(Or(button, button2), Not(And(button, button2)))
val t2 = Not(Or(button, button2, Not(button2)))
val app = ArduinoML(
/*Seq(
Transition(stateOff -> stateOn, t2),
Transition(stateOn -> stateOff, Not(t2))
)*/
Seq(
Transition(stateOff2 -> stateOn, button),
Transition(stateOn -> stateOn2, Not(button)),
Transition(stateOn2 -> stateOff, button),
Transition(stateOff -> stateOff2, Not(button))
)
)
println
println
println(app)
println
println(app.genCode)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment