diff --git a/cmd/mbx/main.go b/cmd/mbx/main.go index 949f3f6..cc58991 100644 --- a/cmd/mbx/main.go +++ b/cmd/mbx/main.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "machine" + "runtime" "time" "github.com/tonygilkerson/marty/pkg/road" @@ -11,9 +12,6 @@ import ( const ( HEARTBEAT_DURATION_SECONDS = 300 - EVENT_DURATION_SECONDS = 3 - TICKER_MS = 1000 - ADC_THRESHOLD = 2_000 ) ///////////////////////////////////////////////////////////////////////////// @@ -40,27 +38,62 @@ func main() { loraRadio := road.SetupLora(machine.SPI0) // - // Init ADC + // Setup charger // - machine.InitADC() // init the machine's ADC subsystem - + + // CHG - Charge status (active low) pulls to GND (open drain) lighting the connected led when the battery is charging. + // If the battery is charged or the charger is disabled, CHG is disconnected from ground (high impedance) and the LED will be off. + chgPin := machine.GP10 + chgPin.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + log.Printf("chgPin status: %v\n", chgPin.Get()) + + // PGOOD - Power Good Status (active low). PGOOD pulls to GND (open drain) lighting the connected led when a valid input source is connected. + // If the input power source is not within specified limits, PGOOD is disconnected from ground (high impedance) and the LED will be off. + pgoodPin := machine.GP11 + pgoodPin.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + log.Printf("pgoodPin status: %v\n", pgoodPin.Get()) // // Setup Mule // - muleADC := machine.ADC{Pin: machine.ADC0} + muleCh := make(chan string) + mulePin := machine.GP12 + mulePin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) + log.Printf("mulePin status: %v\n", mulePin.Get()) + + mulePin.SetInterrupt(machine.PinRising, func(p machine.Pin) { + + // Use non-blocking send so if the channel buffer is full, + // the value will get dropped instead of crashing the system + select { + case muleCh <- "up": + default: + } + + }) // // Setup mail // - mailADC := machine.ADC{Pin: machine.ADC1} + mailCh := make(chan string) + mailPin := machine.GP13 + mailPin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) + log.Printf("mailPin status: %v\n", mulePin.Get()) + + mailPin.SetInterrupt(machine.PinRising, func(p machine.Pin) { + + // Use non-blocking send so if the channel buffer is full, + // the value will get dropped instead of crashing the system + select { + case mailCh <- "up": + default: + } + + }) - // // Launch go routines - // - go mailMonitor(&mailADC, &chLoraTxRx) - go muleMonitor(&muleADC, &chLoraTxRx) - go temperatureMonitor(&muleADC, &chLoraTxRx) + go mailMonitor(&mailCh, &chLoraTxRx) + go muleMonitor(&muleCh, &chLoraTxRx) go road.LoraTx(loraRadio, &chLoraTxRx) // Main loop @@ -69,15 +102,26 @@ func main() { for range ticker.C { log.Printf("------------------MainLoopHeartbeat-------------------- %v", count) - msg := fmt.Sprintf("RoadMainLoopHeartbeat-%v", count) - chLoraTxRx <- msg count += 1 + log.Printf("mailPin status: %v\n", mailPin.Get()) + log.Printf("mulePin status: %v\n", mulePin.Get()) - // // DEVTODO - remove me after test - // for i := 0; i < 5; i++ { - // msg := fmt.Sprintf("BATCH-SEND-%v", i) - // chLoraTxRx <- msg - // } + // + // Send Heartbeat to Tx queue + // + chLoraTxRx <- "RoadMainLoopHeartbeat" + + // + // send charger status + // + sendChargerStatus(chgPin, pgoodPin, &chLoraTxRx) + + // + // Send Temperature to Tx queue + // + sendTemperature(&chLoraTxRx) + + runtime.Gosched() } } @@ -101,78 +145,59 @@ func runLight(led machine.Pin, count int) { } -func mailMonitor(mailADC *machine.ADC, chLoraTxRx *chan string) { - lastEvent := time.Now() - lastHeartbeat := time.Now() - active := false +func mailMonitor(ch *chan string, chLoraTxRx *chan string) { + + for range *ch { + log.Println("Mailbox light up") + *chLoraTxRx <- "MailboxDoorOpened" + + runtime.Gosched() + // Wait a long time to give mail man time to shut the door + time.Sleep(time.Second * 60) + log.Println("Mailbox light down") - ticker := time.NewTicker(time.Millisecond * TICKER_MS) - for range ticker.C { - fmt.Printf("a") - - if mailADC.Get() > ADC_THRESHOLD { - lastEvent = time.Now() - if !active { - active = true - log.Println("Mailbox light rising") - *chLoraTxRx <- "MailboxDoorOpened" - } - } else { - - if active && time.Since(lastEvent) > EVENT_DURATION_SECONDS*time.Second { - active = false - log.Println("Mailbox light falling") - } - - if time.Since(lastHeartbeat) > HEARTBEAT_DURATION_SECONDS*time.Second { - lastHeartbeat = time.Now() - log.Println("Mailbox Heartbeat") - *chLoraTxRx <- "MailboxDoorOpenedHeartbeat" - } - } } + } -func muleMonitor(muleADC *machine.ADC, chLoraTxRx *chan string) { - lastEvent := time.Now() - lastHeartbeat := time.Now() - active := false +func muleMonitor(ch *chan string, chLoraTxRx *chan string) { + + for range *ch { + log.Println("Mule light up") + *chLoraTxRx <- "MuleAlarm" + + runtime.Gosched() + time.Sleep(time.Second * 4) + log.Println("Mule light down") - ticker := time.NewTicker(time.Millisecond * TICKER_MS) - for range ticker.C { - fmt.Printf("U") - - if muleADC.Get() > ADC_THRESHOLD { - lastEvent = time.Now() - if !active { - active = true - log.Println("Mule light rising") - *chLoraTxRx <- "MuleAlarm" - } - } else { - - if active && time.Since(lastEvent) > EVENT_DURATION_SECONDS*time.Second { - active = false - log.Println("Mule light falling") - } - - if time.Since(lastHeartbeat) > HEARTBEAT_DURATION_SECONDS*time.Second { - lastHeartbeat = time.Now() - log.Println("Mule Heartbeat") - *chLoraTxRx <- "MuleAlarmHeartbeat" - } - } } } -func temperatureMonitor(muleADC *machine.ADC, chLoraTxRx *chan string) { +func sendTemperature(chLoraTxRx *chan string) { - ticker := time.NewTicker(time.Second * HEARTBEAT_DURATION_SECONDS) - for range ticker.C { - fmt.Printf("T") - // F = ( (ReadTemperature /1000) * 9/5) + 32 - fahrenheit := ( (machine.ReadTemperature()/1000) * 9/5) + 32 - fmt.Printf("fahrenheit: %v\n", fahrenheit) - *chLoraTxRx <- fmt.Sprintf("MailboxTemperature:%v", fahrenheit) + // F = ( (ReadTemperature /1000) * 9/5) + 32 + fahrenheit := ((machine.ReadTemperature() / 1000) * 9 / 5) + 32 + fmt.Printf("fahrenheit: %v\n", fahrenheit) + *chLoraTxRx <- fmt.Sprintf("MailboxTemperature:%v", fahrenheit) + +} + +func sendChargerStatus(chgPin machine.Pin, pgoodPin machine.Pin, chLoraTxRx *chan string) { + + if pgoodPin.Get() { + log.Println("Power source bad") + *chLoraTxRx <- "ChargerPowerSourceBad" + } else { + log.Println("Power source good") + *chLoraTxRx <- "ChargerPowerSourceGood" } + + if chgPin.Get() { + log.Println("Charger off") + *chLoraTxRx <- "ChargerChargeStatusOff" + } else { + log.Println("Charger on") + *chLoraTxRx <- "ChargerChargeStatusOn" + } + } diff --git a/pkg/road/road.go b/pkg/road/road.go index 1d606e9..ec3fee9 100644 --- a/pkg/road/road.go +++ b/pkg/road/road.go @@ -3,6 +3,7 @@ package road import ( "log" "machine" + "runtime" "time" "tinygo.org/x/drivers/lora" @@ -21,7 +22,7 @@ var ( SX127X_PIN_RST = machine.GP20 SX127X_PIN_CS = machine.GP17 SX127X_PIN_DIO0 = machine.GP21 // (GP21--G0) Must be connected from pico to breakout for radio events IRQ to work - SX127X_PIN_DIO1 = machine.GP22 // (GP22--G1)I don't now what this does, it is assigned but I did not connect form pico to breakout + SX127X_PIN_DIO1 = machine.GP22 // (GP22--G1)I don't now what this does but it seems to need to be connected SX127X_SPI = machine.SPI0 ) @@ -34,8 +35,8 @@ func SetupLora(spi *machine.SPI) *sx127x.Device { spi.Configure(machine.SPIConfig{ SCK: machine.SPI0_SCK_PIN, // GP18 - SDO: machine.SPI0_SDO_PIN, // GP19 - SDI: machine.SPI0_SDI_PIN, // GP16 + SDO: machine.SPI0_SDO_PIN, // GP19 aka MOSI + SDI: machine.SPI0_SDI_PIN, // GP16 aka MISO }) SX127X_SPI.Configure(machine.SPIConfig{Frequency: 500000, Mode: 0}) @@ -79,6 +80,15 @@ func LoraTx(loraRadio *sx127x.Device, ch *chan string) { ticker := time.NewTicker(time.Second * 10) for range ticker.C { + + // + // If there are no messages in the channel then get out quick + // + if len(*ch) == 0 { + log.Println("LoraTx channel is empty, getting out early...") + continue + } + // Enable the radio SX127X_PIN_EN.High() @@ -86,21 +96,22 @@ func LoraTx(loraRadio *sx127x.Device, ch *chan string) { // RX // tStart := time.Now() - log.Println("Receiving Lora for 5 seconds") + log.Println("RX Start - Receiving Lora for 5 seconds") for time.Since(tStart) < 5*time.Second { + // for time.Since(tStart) < 2*time.Second { buf, err := loraRadio.Rx(LORA_DEFAULT_RXTIMEOUT_MS) if err != nil { log.Println("RX Error: ", err) } else if buf != nil { - log.Println("Packet Received: ", string(buf)) + log.Println("Packet Received: ", buf) } } - log.Println("End Lora RX") + log.Println("RX End") // // TX // - log.Println("Start Lora TX") + log.Println("TX Start") var batchMsg string // Concatenate all messages separated by \n @@ -123,8 +134,11 @@ func LoraTx(loraRadio *sx127x.Device, ch *chan string) { } } + // + // Now that we have consumed all the messages from the channel Tx + // if len(batchMsg) > 0 { - log.Println("LORA TX: ", batchMsg) + log.Println("TX: ", batchMsg) err := loraRadio.Tx([]byte(batchMsg), LORA_DEFAULT_TXTIMEOUT_MS) if err != nil { log.Println("TX Error:", err) @@ -132,11 +146,12 @@ func LoraTx(loraRadio *sx127x.Device, ch *chan string) { } else { log.Println("nothing to send") } - log.Println("End Lora TX") + log.Println("TX End") // Disable the radio to save power... SX127X_PIN_EN.Low() + runtime.Gosched() } }