define(`TITLE', `First ESP8266 Project: WiFi Power Switch') define(`DATE', `2015-09-14') define(`CONTENT', ` The ESP8266 boards (ESP-01, cmp. http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family#esp-01 ) were lying on my desk for already some time. I never took the time to install the SDK on my Mac. Now I came around nodemcu ( http://www.nodemcu.com/index_en.html) and it is exactly what I was looking for since a while: Lua on a microcontroller. The idea behind is simple: When you are coding on a PC (or a Mac) and you have a rather small problem, a rather simple task to perform, you (or at least: I) would tend to use a scripting language. On a PC my first choice would be Python, other people prefer Perl or even Lua. On a microcontroller I also was faced those simple tasks (like toggling one bit based on an MQTT message) but never had a scripting language available. But here it is. No need to install an SDK on the PC, just an upload tool like nodemcu-uploader.py or esplorer is required. First the nodemcu firmware needs to be installed on the ESP8266 board. I found a very good intro here. My Lua script for this project running on the microcontroller consists of two files: ''init.lua'' and ''mqtt.lua''. Coding Lua for nodemcu isn't really intuitive, at least not when you come from Arduinos or so. You can't simple write a ''setup'' method and do all the initialization within and a ''loop'' method to be continuously run. Doing it this way would either drive you into the chip's watchdog and would consume any available CPU any and leave nothing for the WiFi and TCP/IP stack. You've to work with timers and callback functions. I found two really good articles here and here. What I also had to learn the hard way is that the nodemcu does not leave much space for the script. With a handful of debug messages containing some meaningful text (as it is no problem on the Arduino or Teensy) I over and over run into out of memory messages or even worse in misbehaving code. I finally came to this code:

    -- init.lua
    -- Constants
    SSID    = "MessWLAN"
    APPWD   = "geheim"
    CMDFILE = "mqtt.lua"

    -- Some control variables
    wifiTrys     = 0
    NUMWIFITRYS  = 200

    function launch()
      print("A")
      print("B: " .. wifi.sta.getip())
      tmr.alarm(0, 5000, 0, function() dofile(CMDFILE) end )
    end

    function checkWIFI()
      if ( wifiTrys > NUMWIFITRYS ) then
        print("C")
      else
        ipAddr = wifi.sta.getip()
        if ( ( ipAddr ~= nil ) and  ( ipAddr ~= "0.0.0.0" ) )then
          tmr.alarm( 1 , 500 , 0 , launch )
        else
          tmr.alarm( 0 , 2500 , 0 , checkWIFI)
          print("D: " .. wifiTrys)
          wifiTrys = wifiTrys + 1
        end
      end
    end

    print("E")

    ipAddr = wifi.sta.getip()
    if ( ( ipAddr == nil ) or  ( ipAddr == "0.0.0.0" ) ) then
      print("F")
      wifi.setmode( wifi.STATION )
      wifi.sta.config( SSID , APPWD)
      print("G")
      tmr.alarm( 0 , 2500 , 0 , checkWIFI )
    else
     launch()
    end

    BROKER = "172.16.2.15"
    BRPORT = 1883
    BRUSER = ""
    BRPWD  = ""
    CLIENTID = "ESP8266-" ..  node.chipid()

    SWITCH_PIN = 3

    SWITCH_ID = 1

    topics = {"IoT/Watchdog", "IoT/Switch" .. SWITCH_ID}
    TOPIC_WATCHDOG = 1
    TOPIC_SWITCH = 2

    pub_sem = 0
    current_topic  = 1
    topicsub_delay = 50
    id2 = 0

    switch_state = false
    old_switch_state = true

    function alarm()
      node.restart()
    end

    gpio.mode(SWITCH_PIN, gpio.OUTPUT)
    gpio.write(SWITCH_PIN, gpio.LOW)

    m = mqtt.Client( CLIENTID, 120, BRUSER, BRPWD)
    tmr.alarm(4, 60000, 1, alarm)
    m:connect( BROKER , BRPORT, 0, function(conn)
         print("4")
         mqtt_sub()
    end)

    function mqtt_sub()
         if table.getn(topics) < current_topic then
              run_main_prog()
         else
              m:subscribe(topics[current_topic] , 0, function(conn)
              end)
              current_topic = current_topic + 1
              tmr.alarm(5, topicsub_delay, 0, mqtt_sub )
         end
    end

    function publish_status()
       if pub_sem == 0 then
         pub_sem = 1
         local uptime = tmr.time()
         local switch_state_pres = 0
         if (switch_state) then
            switch_state_pres = 1
         end
         local msg = "{"metadata":{"device":"WiFiSwitch" .. SWITCH_ID .. ""}, "data":{"uptime":" .. uptime .. ", "state":" .. switch_state_pres .. "}}"
         m:publish("IoT/Status/WiFiSwitch" .. SWITCH_ID, msg ,0,0, function(conn)
            print("8: " .. id2 .. ", " .. switch_state_pres)
            pub_sem = 0
            id2 = id2 +1
         end)
       end
    end

    function run_main_prog()
         tmr.alarm(3, 1000, 1, publish_status )
         m:on("message", function(conn, topic, data)
            print(topic .. ":" )
            if (data ~= nil ) then
              print ( data )
              if (topic == topics[TOPIC_SWITCH] and data == "switch on") then
                switch_state = true
              elseif (topic == topics[TOPIC_SWITCH] and data == "switch off") then
                switch_state = false
              elseif (topic == topics[TOPIC_SWITCH] and data == "switch toggle") then
                switch_state = not switch_state
              elseif (topic == topics[TOPIC_WATCHDOG] and data == "WauWau!") then
                print("9")
                tmr.alarm(4, 60000, 1, alarm)
              end

              if (switch_state ~= old_switch_state) then
                old_switch_state = switch_state
                if (switch_state) then
                  gpio.write(SWITCH_PIN, gpio.LOW)
                else
                  gpio.write(SWITCH_PIN, gpio.HIGH)
                end
              end
            end
          end )
    end

Important note: this thing runs on mains power. Don't play with it unless you know exactly what you are doing! ')