REM COMPILER_OPTION NODEBUG REM REVISION 0.08 REM provisionally declared safe to use REM probably could use mroe testing include "pwm.txt" REM ****************************** GLOBALS AND CONSTANTS ************************************* REM ***************** HARDWARE STUFF ********************* GLOBAL current_0 AS BIT = 6,5 GLOBAL current_1 AS BIT = 6,6 GLOBAL led AS BIT = 6,4 GLOBAL sei AS BIT = 11,7 CONSTANT hi AS INTEGER = -1 CONSTANT lo AS INTEGER = 0 GLOBAL fp AS BYTE = 0x48 GLOBAL sp AS BYTE = 0x49 GLOBAL baudrate AS BYTE = 0x81 REM **************** HARDWARE TRACKING STUFF ************* GLOBAL led_state AS BIT GLOBAL stable AS BIT REM ***************** NETWORK STUFF (CONSTANTS & VARIABLES) ********************** REM note that this is risky REM because the stack can grow into this buffer REM ** RX_BUFFER_INFO ** CONSTANT begin_rx_buffer AS BYTE = 0x52 GLOBAL rx_buffer_addr AS BYTE = 0x52 GLOBAL rx_buffer_cmd AS BYTE = 0x53 GLOBAL rx_buffer_d0 AS BYTE = 0x54 GLOBAL rx_buffer_d1 AS BYTE = 0x55 GLOBAL rx_buffer_d2 AS BYTE = 0x56 GLOBAL rx_buffer_d3 AS BYTE = 0x57 GLOBAL rx_buffer_d4 AS BYTE = 0x58 GLOBAL rx_buffer_d5 AS BYTE = 0x59 REM ** TX_BUFFER_INFO ** CONSTANT begin_tx_buffer AS BYTE = 0x4a GLOBAL tx_buffer_addr AS BYTE = 0x4a GLOBAL tx_buffer_cmd AS BYTE = 0x4b GLOBAL tx_buffer_d0 AS BYTE = 0x4c GLOBAL tx_buffer_d1 AS BYTE = 0x4d GLOBAL tx_buffer_d2 AS BYTE = 0x4e GLOBAL tx_buffer_d3 AS BYTE = 0x4f GLOBAL tx_buffer_d4 AS BYTE = 0x50 GLOBAL tx_buffer_d5 AS BYTE = 0x51 REM ** global variables *** CONSTANT net_timeout AS INTEGER = 20 CONSTANT bcast_delay AS INTEGER = 10 CONSTANT bcast_addr AS BYTE = 255 GLOBAL message_size AS BYTE GLOBAL bcast_mode AS BIT GLOBAL bcast_ok AS BIT REM ************** NETWORK COMMANDS **************** CONSTANT cmd_ping AS BYTE = 0x01 CONSTANT cmd_ping_reply AS BYTE = 0x02 CONSTANT cmd_prep AS BYTE = 0x03 CONSTANT cmd_prep_ok AS BYTE = 0x04 CONSTANT cmd_unknown_cmd AS BYTE = 0x05 CONSTANT cmd_write AS BYTE = 0x06 CONSTANT cmd_write_ok AS BYTE = 0x07 CONSTANT cmd_write_error AS BYTE = 0x08 CONSTANT cmd_read AS BYTE = 0x09 CONSTANT cmd_read_ok AS BYTE = 0x0A CONSTANT cmd_system_error AS BYTE = 0x66 CONSTANT cmd_read_raw AS BYTE = 0x0B CONSTANT cmd_setup_charger AS BYTE = 0x0C CONSTANT cmd_engage_charger AS BYTE = 0x0D CONSTANT cmd_engage_charger_err AS BYTE = 0x0E CONSTANT cmd_engage_charger_ok AS BYTE = 0x0F CONSTANT cmd_charger_params AS BYTE = 0x10 CONSTANT cmd_setup_phase AS BYTE = 0x11 CONSTANT cmd_engage_phase AS BYTE = 0x12 CONSTANT cmd_engage_phase_ok AS BYTE = 0x14 CONSTANT cmd_manual_charge_control AS BYTE = 0x15 CONSTANT cmd_manual_charge_control_ok AS BYTE = 0x16 CONSTANT cmd_shutdown_charger AS BYTE = 0x17 CONSTANT cmd_turn_on_led AS BYTE = 0x18 CONSTANT cmd_turn_off_led AS BYTE = 0x19 CONSTANT cmd_kill_charger_ok AS BYTE = 0x1A CONSTANT cmd_read_sensor_data AS BYTE = 0x1B CONSTANT cmd_kill_charger AS BYTE = 0x1C CONSTANT cmd_reboot AS BYTE = 0x1D CONSTANT cmd_read_cooked AS BYTE = 0x1E CONSTANT cmd_read_cooked_ok AS BYTE = 0x1F CONSTANT cmd_read_charger_params AS BYTE = 0xDF CONSTANT cmd_cascading_read_cooked AS BYTE = 0xF0 CONSTANT cmd_write_block AS BYTE = 0xF1 CONSTANT cmd_write_block_err AS BYTE = 0xF2 CONSTANT cmd_write_block_ok AS BYTE = 0xF3 CONSTANT cmd_read_block AS BYTE = 0xF4 CONSTANT cmd_read_block_ok AS BYTE = 0xF5 CONSTANT cmd_execute_loader AS BYTE = 0xF6 CONSTANT cmd_execute_loader_err AS BYTE = 0xF7 CONSTANT cmd_clear_error AS BYTE = 0xF8 CONSTANT cmd_set_baudrate AS BYTE = 0xF9 CONSTANT cmd_cascading_clear_error AS BYTE = 0xFA CONSTANT cmd_cascading_prep AS BYTE = 0xFB CONSTANT cmd_cascading_set_baudrate AS BYTE = 0xFC REM **************** error flags ******************** CONSTANT err_overtemp AS BYTE = 1 CONSTANT err_undertemp AS BYTE = 2 CONSTANT err_uncommanded_charge AS BYTE = 4 CONSTANT err_overvolt AS BYTE = 8 CONSTANT err_undervolt AS BYTE = 16 CONSTANT err_batt_overtemp AS BYTE = 32 CONSTANT err_batt_undertemp AS BYTE = 64 CONSTANT err_crash AS BYTE = 128 REM **************** REASONS TO KILL **************** CONSTANT reason_init AS BYTE = 1 CONSTANT reason_netcmd AS BYTE = 2 CONSTANT reason_netcmd_kill AS BYTE = 3 CONSTANT reason_overtemp AS BYTE = 4 CONSTANT reason_hv_under_limit AS BYTE = 5 CONSTANT reason_time_elapsed_1 AS BYTE = 6 CONSTANT reason_outside_temp_limit AS BYTE = 7 CONSTANT reason_outside_volts_limit AS BYTE = 8 CONSTANT reason_outside_amps_limit AS BYTE = 9 CONSTANT reason_didt AS BYTE = 10 CONSTANT reason_charge_error_inhibited AS BYTE = 11 CONSTANT reason_gone_thru_all_phases AS BYTE = 20 CONSTANT reason_hv_bal_under_limit AS BYTE = 19 CONSTANT reason_flag_shutdown AS BYTE = 100 CONSTANT reason_volts_down AS BYTE = 12 CONSTANT reason_volts_up AS BYTE = 13 CONSTANT reason_amps_down AS BYTE = 14 CONSTANT reason_amps_up AS BYTE = 15 CONSTANT reason_watts_down AS BYTE = 16 CONSTANT reason_scale_back_down AS BYTE = 17 REM **************** OTHER STUFF ******************** REM we do something cute with these.. REM write_addr's MSB (0x8000) indicates EEPROM or RAM REM since we're promised no EEPROM addresses beyond REM 0x1FFF REM also note that these addresses get reused for enable_charger REM they're really kind of a network 'catch-all' GLOBAL write_addr AS INTEGER GLOBAL write_data AS BYTE REM the most stack we're allowed to use now is 37 REM which equates to about four levels deep of function calls REM remember that local variables are placed on the stack ! REM ***************** CHARGER STUFF *********************** GLOBAL i AS BYTE GLOBAL last_sec AS BYTE REM these all contain averages. Instantaneous input is averaged in GLOBAL ain0 AS BYTE GLOBAL ain1 AS BYTE GLOBAL ain2 AS BYTE GLOBAL ain3 AS BYTE GLOBAL vfin AS INTEGER GLOBAL charge_time_hi AS BYTE GLOBAL charge_time_lo AS BYTE GLOBAL current_amps AS BYTE GLOBAL current_volts AS BYTE GLOBAL current_watts AS BYTE GLOBAL current_temp AS BYTE GLOBAL charge_phase AS BYTE GLOBAL pwm_out AS BYTE GLOBAL errno AS BYTE GLOBAL timeout AS BYTE GLOBAL current_hv_volts AS BYTE GLOBAL charged AS BIT GLOBAL didt_amps AS BYTE GLOBAL didt_up AS BYTE GLOBAL sec_stop AS BYTE GLOBAL auto_charge_engaged AS BIT GLOBAL auto_bal_engaged AS BIT REM *** CHARGE FLAGS *** CONSTANT chg_flag_shutdown AS BYTE = 1 CONSTANT chg_flag_temp_comp AS BYTE = 2 CONSTANT chg_flag_didt AS BYTE = 4 CONSTANT chg_flag_charged AS BYTE = 8 CONSTANT throttle_up AS BYTE = 2 CONSTANT throttle_down AS BYTE = 1 REM ****************** CONFIG STUFF *********************** DIM my_net_addr[1] AS BYTE = 0x1FFF DIM last_error[1] AS BYTE = 0x1FFE DIM last_message_size[1] AS BYTE = 0x1FFD DIM num_samples[1] AS BYTE = 0x1FFC DIM num_vf[1] AS BYTE = 0x1FFB DIM dly_vf[1] AS BYTE = 0x1FFA DIM div_vf[1] AS BYTE = 0x1FF9 DIM dl_lock[1] AS BYTE = 0x1FF8 DIM pwm_freq[1] AS INTEGER = 0x1FF6 DIM pwm_offset[1] AS BYTE = 0x1FF5 DIM avg_num[1] AS BYTE = 0x1FF4 DIM config_locked[1] AS BYTE = 0x1FF3 DIM pwm_seed[1] AS BYTE = 0x1FF2 DIM last_errno[2] AS BYTE = 0x1FF0 REM master limits DIM max_watts[1] AS INTEGER = 0x1FE0 DIM max_volts[1] AS BYTE = 0x1FE2 DIM max_amps[1] AS BYTE = 0x1FE3 DIM max_chg_temp[1] AS BYTE = 0x1FE4 DIM max_batt_temp[1] AS BYTE = 0x1FE5 DIM phases_enabled[1] AS BYTE = 0x1FE6 DIM scale_back_chg_temp[1] AS BYTE= 0x1FE7 DIM charge_inhibit_errno_mask[1] AS BYTE = 0x1FE8 REM timeout stuff REM new for 0.06 DIM timeout_minutes[1] AS BYTE = 0x1FE9 DIM sleep_seconds[1] AS INTEGER = 0x1FEA DIM version[1] AS BYTE = 0x1FEB DIM phaselog[4] AS BYTE = 0x1FEC REM phase stuff DIM phase_charge_volts[8] AS BYTE = 0x1F00 DIM phase_charge_amps[8] AS BYTE = 0x1F08 DIM phase_charge_flags[8] AS BYTE= 0x1F10 DIM phase_charge_time_lo[8] AS BYTE = 0x1F18 DIM phase_charge_time_hi[8] AS BYTE = 0x1F20 DIM phase_charge_volts_limit_lo[8] AS BYTE = 0x1F28 DIM phase_charge_volts_limit_hi[8] AS BYTE = 0x1F30 DIM phase_charge_amps_limit_lo[8] AS BYTE = 0x1F38 DIM phase_charge_amps_limit_hi[8] AS BYTE= 0x1F40 DIM phase_charge_temp_limit_lo[8] AS BYTE = 0x1F48 DIM phase_charge_temp_limit_hi[8] AS BYTE = 0x1F50 REM calibration stuff DIM volts_m[1] AS INTEGER = 0x1FD0 DIM volts_c[1] AS INTEGER = 0x1FD2 DIM volts_b[1] AS INTEGER = 0x1FD4 DIM amps_m[1] AS INTEGER = 0x1FD6 DIM amps_c[1] AS INTEGER = 0x1FD8 DIM amps_b[1] AS INTEGER = 0x1FDA DIM volts_hv_m[1] AS INTEGER = 0x1FB0 DIM volts_hv_c[1] AS INTEGER = 0x1FB2 DIM volts_hv_b[1] AS INTEGER = 0x1FB4 DIM temp_lookup[256] AS BYTE = 0x1E00 DIM temp_comp[256] AS INTEGER = 0x1C00 REM turn_on_charger_stuff DIM turn_on_balance_hv[1] AS BYTE = 0x1FB6 DIM turn_on_charge_hv[1] AS BYTE = 0x1FB7 DIM turn_on_balance_phase[1] AS BYTE = 0x1FB8 DIM turn_on_charge_phase[1] AS BYTE = 0x1FB9 DIM turn_off_charge_hv[1] AS BYTE = 0x1FBA DIM clear_charged_thresh[1] AS BYTE = 0x1FBB DIM didt_window[1] AS BYTE = 0x1FBC DIM didt_up_limit[1] AS BYTE = 0x1FBD DIM didt_climb[1] AS BYTE = 0x1FBE REM other stuff DIM beep_on_net_timeout[1] AS BYTE = 0x1FBF DIM baud[1] AS BYTE = 0x1FAB DIM baud_locked[1] AS BYTE = 0x1FAA REM last_minute_additions DIM min_volts[1] AS BYTE = 0x1FAF DIM min_batt_temp[1] AS BYTE = 0x1FAE DIM min_chg_temp[1] AS BYTE = 0x1FAD DIM balance_num_batteries_phase[1] AS BYTE = 0x1FA9 DIM balance_num_batteries[1] AS BYTE = 0x1FA8 DIM last_chg_abort[4] AS BYTE = 0x1F58 DIM last_throttle[4] AS BYTE = 0x1F5C DIM turn_off_balance_hv[1] AS BYTE = 0x1F60 REM ***************************** LIBRARY ROUTINES BEGIN HERE ********************* VITAL SUBROUTINE transmit() BPRINT tx_buffer_addr, tx_buffer_cmd, tx_buffer_d0, tx_buffer_d1, tx_buffer_d2, tx_buffer_d3, tx_buffer_d4, tx_buffer_d5 bcast_mode = 0 END VITAL SUBROUTINE reset_rx() message_size = 0 SET COM1 TO 1 END SUBROUTINE report_error(err AS BYTE) REM new for 0.06 IF(errno AND err) REM if we already know about it ELSE errno = errno OR err last_errno[0] = last_errno[1] last_errno[1] = errno ENDIF REM we really should REM A) beep about it REM B) inhibit charging right here, instead of waiting for REM the next throttle END FUNCTION didt_inhibited() AS BYTE IF (phase_charge_flags[(charge_phase - 1)] AND chg_flag_didt) IF(phase_charge_time_hi[(charge_phase -1)] = charge_time_hi) AND (phase_charge_time_lo[(charge_phase - 1)] > (charge_time_lo + didt_window[0])) didt_inhibited = 1 ELSE didt_inhibited = 0 ENDIF ELSE didt_inhibited = 1 ENDIF END SUBROUTINE td(reason AS BYTE) IF (last_throttle[3] = 0) AND (reason <> last_throttle[0]) last_throttle[3] = last_throttle[2] last_throttle[2] = last_throttle[1] last_throttle[1] = last_throttle[0] last_throttle[0] = reason ENDIF END VITAL SUBROUTINE turn_off_charger(reason AS BYTE) current_0 = lo current_1 = lo REM 0.08 - seed current at 0 ain3 = 0 auto_charge_engaged = lo auto_bal_engaged = lo sec_stop = PEEK(40) pwm_out = 0 charge_phase = 0 IF last_chg_abort[3] = 0 last_chg_abort[3] = last_chg_abort[2] last_chg_abort[2] = last_chg_abort[1] last_chg_abort[1] = last_chg_abort[0] last_chg_abort[0] = reason ENDIF END VITAL SUBROUTINE turn_on_led() led_state = lo led = lo END VITAL SUBROUTINE turn_off_led() led_state = hi led = hi END VITAL SUBROUTINE beep() LOCAL i AS BYTE FOR i = 0 TO 100 led = lo led = hi NEXT led = led_state END VITAL SUBROUTINE shut_down_charger(reason AS BYTE) sec_stop = PEEK(40) ain3 = 0 auto_charge_engaged = lo auto_bal_engaged = lo current_0 = lo current_1 = lo pwm_out = 0 charge_phase = 0 pwm(0,0) IF last_chg_abort[3] = 0 last_chg_abort[3] = last_chg_abort[2] last_chg_abort[2] = last_chg_abort[1] last_chg_abort[1] = last_chg_abort[0] last_chg_abort[0] = reason ENDIF END FUNCTION real_volts(involts AS BYTE) AS INTEGER real_volts = (involts/20) + 5 REM rough approximation only, for use in calculating watts END FUNCTION real_amps(inamps AS BYTE) AS INTEGER real_amps = inamps / 10 END VITAL FUNCTION charge_inhibited() AS BYTE IF (errno AND charge_inhibit_errno_mask[0]) charge_inhibited = hi turn_on_led() ELSE charge_inhibited = lo turn_off_led() ENDIF END VITAL SUBROUTINE turn_on_charger() current_0 = hi current_1 = hi pwm_out = pwm_seed[0] : REM so we don't grow old waiting for the REM power to come on END VITAL SUBROUTINE engage_phase(phase AS BYTE) IF (phaselog[3] = 0) phaselog[3] = phaselog[2] phaselog[2] = phaselog[1] phaselog[1] = phaselog[0] phaselog[0] = phase ENDIF IF (phase > 8) beep() RETURN ENDIF IF charge_inhibited() REM something beep() ELSE timeout = 0 didt_amps = 0 didt_up = 0 charge_time_lo = phase_charge_time_lo[(phase - 1)] charge_time_hi = phase_charge_time_hi[(phase - 1)] charge_phase = phase turn_on_charger() ENDIF END SUBROUTINE end_of_charge_segment(reason AS BYTE) REM determine if we are in a network commanded charge or a local controlled charge REM just to be confusing, charge_phase 0 is OFF REM charge phases are 1 more than their array element IF last_chg_abort[3] = 0 last_chg_abort[3] = last_chg_abort[2] last_chg_abort[2] = last_chg_abort[1] last_chg_abort[1] = last_chg_abort[0] last_chg_abort[0] = reason ENDIF IF charge_phase = 8 REM remotely commanded charge shut_down_charger(reason) ELSE REM locally controlled charge IF (phase_charge_flags[(charge_phase - 1)] AND chg_flag_charged) charged = hi ENDIF IF (phase_charge_flags[(charge_phase - 1)] AND chg_flag_shutdown) shut_down_charger(reason + reason_flag_shutdown) ELSE DO WHILE charge_phase < 8 charge_phase = charge_phase + 1 IF ((phases_enabled[0] <> 0) AND (2^(charge_phase - 1))) REM changed in 0.08 because the last version REM made NO sense EXIT ENDIF LOOP IF charge_phase = 8 shut_down_charger(reason + reason_gone_thru_all_phases) ELSE engage_phase(charge_phase) ENDIF ENDIF ENDIF END FUNCTION convert_input_amps(input_data AS BYTE) AS BYTE REM new for 0.05 LOCAL kamps AS INTEGER IF(input_data < amps_b[0]) REM new for 0.06 - keeps from rolling convert_input_amps = 0 ELSE kamps = (amps_m[0] * (input_data - amps_b[0])) + amps_c[0] convert_input_amps = MIN((kamps / 100),255) ENDIF END FUNCTION convert_input_hv_volts(input_data AS BYTE) AS BYTE REM new for 0.06 REM returns current voltage / 2 REM i.e. 120 = 60 REM i.e. 330 = 115 LOCAL khvvolts AS INTEGER khvvolts = (volts_hv_m[0] * (input_data - volts_hv_b[0])) + volts_hv_c[0] convert_input_hv_volts = ((khvvolts / 10)/2) END FUNCTION convert_input_volts(input_data AS BYTE) AS BYTE REM new for 0.05 LOCAL kvolts AS INTEGER LOCAL ret AS BYTE kvolts = (volts_m[0] * (input_data - volts_b[0])) + volts_c[0] ret = (kvolts - 5120) / 50 IF charged AND (ret < clear_charged_thresh[0]) charged = lo ENDIF convert_input_volts = ret END FUNCTION current_tvolts() AS INTEGER REM new for 0.06 REM return 138 for 13.8 volts LOCAL tvolts AS INTEGER tvolts = convert_input_volts(ain2) current_tvolts = ((tvolts * 50) + 5120) / 100 END FUNCTION convert_input_volts_temp_comp(input_data AS BYTE) AS BYTE REM new for 0.06 LOCAL kvolts AS INTEGER LOCAL ret AS BYTE kvolts = (volts_m[0] * (input_data - volts_b[0])) + volts_c[0] kvolts = kvolts + temp_comp[current_temp] ret = (kvolts - 5120) / 50 convert_input_volts_temp_comp = ret END FUNCTION convert_input_temp(input_data AS BYTE) AS BYTE convert_input_temp = temp_lookup[input_data] END SUBROUTINE poll_inputs() REM collect data LOCAL i AS BYTE LOCAL j AS INTEGER FOR i = 0 to num_samples[0] ain0 = (((ain0 * (avg_num[0] - 1)) + AIN(0)) / avg_num[0]) ain1 = (((ain1 * (avg_num[0] - 1)) + AIN(1)) / avg_num[0]) ain2 = (((ain2 * (avg_num[0] - 1)) + AIN(2)) / avg_num[0]) ain3 = (((ain3 * (avg_num[0] - 1)) + AIN(3)) / avg_num[0]) NEXT FOR i = 0 to num_vf[0] j = (COUNT(6,7,dly_vf[0]) / div_vf[0]) vfin = ((vfin * (avg_num[0] - 1)) + j) / avg_num[0] NEXT current_amps = convert_input_amps(ain3) current_volts = convert_input_volts(ain2) current_hv_volts = convert_input_hv_volts(vfin AND 255) current_watts = real_volts(current_volts) * real_amps(current_amps) current_temp = convert_input_temp(ain1) REM new for 0.06 IF (min_volts[0] AND (current_volts < min_volts[0])) report_error(err_undervolt) ENDIF END SUBROUTINE sixty_second_charger_stuff() LOCAL didt_climber AS BYTE IF (stable = lo) stable = hi RETURN ENDIF IF (charge_phase = 0 AND config_locked[0] = 69) IF((charged = lo) AND (turn_on_charge_hv[0] <> 0) AND (turn_on_charge_phase[0] <> 0) AND (current_hv_volts > turn_on_charge_hv[0])) engage_phase(turn_on_charge_phase[0]) auto_charge_engaged = hi stable = lo ELSE beep() beep() IF((turn_on_balance_hv[0] <> 0) AND (turn_on_balance_phase[0] <> 0) AND (current_hv_volts > turn_on_balance_hv[0])) engage_phase(turn_on_balance_phase[0]) auto_bal_engaged = hi ENDIF IF((balance_num_batteries[0] <> 0) AND balance_num_batteries_phase[0]) IF ((current_hv_volts * 20) > (balance_num_batteries[0] * current_tvolts())) engage_phase(balance_num_batteries_phase[0]) ENDIF ENDIF ENDIF ENDIF IF((auto_charge_engaged = hi) AND (turn_off_charge_hv[0] <> 0) AND (current_hv_volts < turn_off_charge_hv[0])) turn_off_charger(reason_hv_under_limit) ENDIF IF((auto_bal_engaged = hi) AND (turn_off_balance_hv[0] <> 0) AND (current_hv_volts < turn_off_balance_hv[0])) turn_off_charger(reason_hv_bal_under_limit) ENDIF IF (didt_inhibited() <> 1) IF(current_amps < didt_climber) didt_climber = current_amps ELSE didt_climber = didt_climb[0] ENDIF IF ((didt_amps <> 0) AND ((current_amps - didt_climber) > didt_amps)) didt_up = didt_up + 1 IF(didt_up = didt_up_limit[0]) end_of_charge_segment(reason_didt) ENDIF ELSE didt_amps = current_amps ENDIF ENDIF END SUBROUTINE one_second_charger_stuff() REM handle timer LOCAL throttle AS BYTE timeout = 0 IF charge_inhibited() charge_time_hi = 0 charge_time_lo = 0 charge_phase = 0 ENDIF IF (charge_time_hi OR charge_time_lo) IF (charge_time_hi <> 0xFF) OR (charge_time_lo <> 0xFF) IF (charge_time_lo = 1) IF (charge_time_hi = 0) charge_time_lo = 0 end_of_charge_segment(reason_time_elapsed_1) ELSE charge_time_hi = charge_time_hi - 1 charge_time_lo = 255 ENDIF ELSE charge_time_lo = charge_time_lo - 1 ENDIF ENDIF ENDIF IF (charge_time_hi OR charge_time_lo) AND charge_phase REM handle throttle throttle = 0 current_amps = convert_input_amps(ain3) current_watts = ((real_volts(current_volts) * real_amps(current_amps)) AND 255) current_temp = convert_input_temp(ain1) IF (phase_charge_flags[(charge_phase -1)] AND chg_flag_temp_comp) current_volts = convert_input_volts_temp_comp(ain2) ELSE current_volts = convert_input_volts(ain2) ENDIF REM todo: put heatsink temp limit here REM check to see if there is a limit spec'd in the flags IF(((phase_charge_volts[(charge_phase - 1)] <> 0) AND (current_volts > phase_charge_volts[(charge_phase - 1)])) OR current_volts > max_volts[0]) throttle = throttle OR throttle_down td(reason_volts_down) ELSE IF(((phase_charge_volts[(charge_phase - 1)] <> 0) AND (current_volts < phase_charge_volts[(charge_phase - 1)])) AND (current_volts < max_volts[0])) throttle = throttle OR throttle_up td(reason_volts_up) ENDIF ENDIF IF(((phase_charge_amps[(charge_phase - 1)] <> 0) AND (current_amps > phase_charge_amps[(charge_phase - 1)])) OR current_amps > max_amps[0]) throttle = throttle OR throttle_down td(reason_amps_down) ELSE IF((phase_charge_amps[(charge_phase - 1)] <> 0) AND (current_amps < phase_charge_amps[(charge_phase - 1)])) AND (current_amps < max_amps[0]) throttle = throttle OR throttle_up td(reason_amps_up) ENDIF ENDIF IF (ain0 < scale_back_chg_temp[0]) throttle = throttle OR throttle_down td(reason_scale_back_down) ENDIF REM IF (max_watts[0] <> 0) REM IF (current_watts > max_watts[0]) REM throttle = throttle OR throttle_down REM td(reason_watts_down) REM beep() REM ENDIF REM ENDIF REM this should all be replaced with something that automagically does 128, 64, 32, 16, etc IF(throttle AND throttle_down) REM o/~ Riding that train, high on cocaine.. REM ... Casey Jones, you better watch youur amps o/~ REM --The EVer Greatful Dead IF(pwm_out) pwm_out = pwm_out - 1 ENDIF ELSE IF(throttle AND throttle_up) IF(pwm_out < 255) pwm_out = pwm_out + 1 ENDIF ENDIF ENDIF REM check to see if it's time to shut down based on charger limits REM by our definition, we will not shutdown on a low limit with a throttle up REM and we will not shutdown on a high limit with a throttle down IF(stable = hi) IF ((phase_charge_temp_limit_lo[(charge_phase - 1)] <> 0) AND (current_temp <= phase_charge_temp_limit_lo[(charge_phase - 1)])) end_of_charge_segment(reason_outside_temp_limit) ENDIF IF ((phase_charge_temp_limit_hi[(charge_phase - 1)] <> 0) AND (current_temp > phase_charge_temp_limit_hi[(charge_phase - 1)])) end_of_charge_segment(reason_outside_temp_limit) ENDIF IF ((phase_charge_volts_limit_lo[(charge_phase - 1)] <> 0) AND (NOT (throttle AND throttle_up)) AND (current_volts <= phase_charge_volts_limit_lo[(charge_phase - 1)])) end_of_charge_segment(reason_outside_volts_limit) ENDIF IF ((phase_charge_volts_limit_hi[(charge_phase - 1)] <> 0) AND (NOT (throttle AND throttle_down)) AND (current_volts > phase_charge_volts_limit_hi[(charge_phase - 1)])) end_of_charge_segment(reason_outside_volts_limit) ENDIF IF ((phase_charge_amps_limit_lo[(charge_phase - 1)] <> 0) AND (NOT (throttle AND throttle_up)) AND (current_amps <= phase_charge_amps_limit_lo[(charge_phase - 1)])) end_of_charge_segment(reason_outside_amps_limit) ENDIF IF ((phase_charge_amps_limit_hi[(charge_phase - 1)] <> 0) AND (NOT (throttle AND throttle_down)) AND (current_amps > phase_charge_amps_limit_hi[(charge_phase - 1)])) end_of_charge_segment(reason_outside_amps_limit) ENDIF ENDIF IF(ain0 < max_chg_temp[0]) shut_down_charger(reason_overtemp) report_error(err_overtemp) ENDIF IF(charge_phase <> 0) pwm((pwm_offset[0] + (pwm_out * 2)),pwm_freq[0]) ENDIF ENDIF END SUBROUTINE check_for_errors() REM check to see if there are any serious error conditions IF (charge_phase = 0) AND (current_amps > 0) AND (ABS(PEEK(40) - sec_stop) > 20) report_error(err_uncommanded_charge) ENDIF IF ((max_batt_temp[0] <> 0) AND (current_temp > max_batt_temp[0])) report_error(err_batt_overtemp) ENDIF IF ((min_batt_temp[0] <> 0) AND (current_temp < min_batt_temp[0])) report_error(err_batt_undertemp) ENDIF IF ((max_chg_temp[0] <> 0) AND (ain0 < max_chg_temp[0])) report_error(err_overtemp) REM NOTE: BACKWARDS AND NOT THRU LOOKUP TABLE ENDIF IF ((min_chg_temp[0] <> 0) AND (ain0 > min_chg_temp[0])) report_error(err_undertemp) REM NOTE: BACKWARDS AND NOT THRU LOOKUP TABLE ENDIF IF ((max_volts[0] <> 0) AND (current_volts > max_volts[0])) report_error(err_overvolt) ENDIF IF ((min_volts[0] <> 0) AND (current_volts < min_volts[0])) report_error(err_undervolt) ENDIF IF (last_error[0]) report_error(err_crash) ENDIF END SUBROUTINE handle_network() LOCAL i AS BYTE LOCAL j AS INTEGER REM first see if it is to us or broadcast timeout = 0 IF ((rx_buffer_addr = my_net_addr[0]) OR (rx_buffer_addr = bcast_addr)) REM yep, it's to us, let's do something about it REM this code trades speed for EEPROM usage - changed in 0.04 tx_buffer_addr = my_net_addr[0] tx_buffer_d0 = 0 tx_buffer_d1 = 0 tx_buffer_d2 = 0 tx_buffer_d3 = 0 tx_buffer_d4 = 0 tx_buffer_d5 = 0 SELECT rx_buffer_cmd CASE cmd_ping FOR i = 2 TO 7 POKE(begin_tx_buffer+i,PEEK(begin_rx_buffer+i)) NEXT sixty_second_charger_stuff() tx_buffer_cmd = cmd_ping_reply CASE cmd_read j = (rx_buffer_d0 * 256) + rx_buffer_d1 tx_buffer_cmd = cmd_read_ok IF(j AND 0x8000) j = j - 0x8000 tx_buffer_d0 = EEPROM_PEEK(j) ELSE tx_buffer_d0 = PEEK(j) ENDIF CASE cmd_set_baudrate IF(rx_buffer_d0 <> write_data) tx_buffer_cmd = cmd_write_error tx_buffer_d0 = rx_buffer_d0 tx_buffer_d1 = write_data ELSE tx_buffer_cmd = cmd_read_ok baudrate = rx_buffer_d0 ENDIF CASE cmd_prep REM prep is a special case REM really means 'prepare to write' REM the idea is we don't want to write either RAM or EEPROM REM without some kind of safety check first write_addr = (rx_buffer_d0 * 256) + rx_buffer_d1 write_data = rx_buffer_d2 tx_buffer_addr = my_net_addr[0] tx_buffer_cmd = cmd_prep_ok tx_buffer_d0 = (write_addr + write_data) AND 0xFF tx_buffer_d1 = rx_buffer_d0 tx_buffer_d2 = rx_buffer_d1 tx_buffer_d3 = rx_buffer_d2 tx_buffer_d4 = sp CASE cmd_write REM write is a bit funky j = (rx_buffer_d0 * 256) + rx_buffer_d1 i = rx_buffer_d2 IF (j <> write_addr) OR (i <> write_data) tx_buffer_cmd = cmd_write_error tx_buffer_d0 = (write_addr AND 0xFF00) / 256 tx_buffer_d1 = (write_addr AND 0x00FF) tx_buffer_d2 = write_data tx_buffer_d3 = rx_buffer_d0 tx_buffer_d4 = rx_buffer_d1 tx_buffer_d5 = rx_buffer_d2 ELSE IF(write_addr AND 0x8000) write_addr = write_addr - 0x8000 EEPROM_POKE(write_addr,write_data) ELSE POKE((write_addr AND 0x00FF),write_data) ENDIF tx_buffer_cmd = cmd_write_ok tx_buffer_d0 = rx_buffer_d0 tx_buffer_d1 = rx_buffer_d1 tx_buffer_d2 = rx_buffer_d2 ENDIF CASE cmd_read_raw REM 0.04 made this return a special data type tx_buffer_cmd = cmd_read_sensor_data tx_buffer_d0 = ain0 tx_buffer_d1 = ain1 tx_buffer_d2 = ain2 tx_buffer_d3 = ain3 tx_buffer_d4 = (vfin AND 0xFF00) / 256 tx_buffer_d5 = (vfin AND 0x00FF) CASE cmd_setup_phase REM new for 0.02 write_data = rx_buffer_d0 IF write_data > 0 AND write_data < 8 tx_buffer_cmd = cmd_read_ok tx_buffer_d0 = phase_charge_volts[write_data] tx_buffer_d1 = phase_charge_amps[write_data] tx_buffer_d2 = phase_charge_time_lo[write_data] tx_buffer_d3 = phase_charge_time_hi[write_data] tx_buffer_d4 = phase_charge_flags[write_data] ELSE tx_buffer_cmd = cmd_write_error : REM okay, so it's inconsistant tx_buffer_d0 = write_data ENDIF CASE cmd_engage_phase IF(rx_buffer_d0 <> write_data) tx_buffer_cmd = cmd_write_error : REM okay, so it's inconsistant tx_buffer_d0 = write_data ELSE tx_buffer_cmd = cmd_engage_phase_ok tx_buffer_d0 = phase_charge_volts[write_data] tx_buffer_d1 = phase_charge_amps[write_data] tx_buffer_d2 = phase_charge_time_lo[write_data] tx_buffer_d3 = phase_charge_time_hi[write_data] tx_buffer_d4 = phase_charge_flags[write_data] auto_charge_engaged = lo auto_bal_engaged = lo engage_phase(write_data) ENDIF CASE cmd_read_cooked tx_buffer_cmd = cmd_read_cooked_ok tx_buffer_d0 = current_volts tx_buffer_d1 = current_amps tx_buffer_d2 = current_watts tx_buffer_d3 = current_temp tx_buffer_d4 = current_hv_volts tx_buffer_d5 = errno CASE cmd_cascading_read_cooked tx_buffer_cmd = cmd_cascading_read_cooked tx_buffer_addr = my_net_addr[0] + 1 tx_buffer_d0 = current_volts tx_buffer_d1 = current_amps tx_buffer_d2 = current_temp tx_buffer_d3 = charge_phase tx_buffer_d4 = pwm_out tx_buffer_d5 = errno CASE cmd_write_block j = (rx_buffer_d0 * 256) + rx_buffer_d1 IF ((j <> write_addr) OR (config_locked[0] <> 55)) tx_buffer_cmd = cmd_write_block_err tx_buffer_d0 = (write_addr AND 0xFF00) / 256 tx_buffer_d1 = write_addr tx_buffer_d2 = rx_buffer_d0 tx_buffer_d3 = rx_buffer_d1 tx_buffer_d4 = config_locked[0] ELSE EEPROM_POKE(write_addr,rx_buffer_d2) EEPROM_POKE((write_addr+1),rx_buffer_d3) EEPROM_POKE((write_addr+2),rx_buffer_d4) EEPROM_POKE((write_addr+3),rx_buffer_d5) tx_buffer_cmd = cmd_write_block_ok tx_buffer_d0 = sp ENDIF CASE cmd_read_block j = (rx_buffer_d0 * 256) + rx_buffer_d1 tx_buffer_cmd = cmd_read_block_ok tx_buffer_d0 = rx_buffer_d0 tx_buffer_d1 = rx_buffer_d1 tx_buffer_d2 = EEPROM_PEEK(j) tx_buffer_d3 = EEPROM_PEEK(j+1) tx_buffer_d4 = EEPROM_PEEK(j+2) tx_buffer_d5 = EEPROM_PEEK(j+3) CASE cmd_execute_loader REM USE WITH EXTREME CARE! REM it is quite possible to crash the board this way REM new for 0.06 REM never been tested, and I don't have any good way to test REM nor time, right now REM but theoretically should work j = (rx_buffer_d0 * 256) + rx_buffer_d1 i = PEEK(j-1) IF((i<>rx_buffer_d2) OR (config_locked[0] <> 121) OR (write_addr <> j)) tx_buffer_cmd = cmd_execute_loader_err tx_buffer_d0 = rx_buffer_d0 tx_buffer_d1 = rx_buffer_d1 tx_buffer_d2 = rx_buffer_d2 tx_buffer_d3 = i tx_buffer_d4 = config_locked[0] ELSE GOSUB(j) ENDIF CASE cmd_setup_charger tx_buffer_cmd = cmd_read_ok REM 0.02 - added charge limit support REM 0.03 - moved the whole shebang into EEPROM phase_charge_volts[7] = rx_buffer_d0 phase_charge_amps[7] = rx_buffer_d1 phase_charge_volts_limit_lo[7] = 0 phase_charge_volts_limit_hi[7] = 0 phase_charge_amps_limit_lo[7] = 0 phase_charge_amps_limit_hi[7] = 0 phase_charge_temp_limit_lo[7] = 0 phase_charge_temp_limit_hi[7] = 0 REM the truly clever can use eeprom_write to setup REM these parameters after things get going write_addr = rx_buffer_d2 + (rx_buffer_d3 * 256) phase_charge_flags[7] = rx_buffer_d4 tx_buffer_d0 = rx_buffer_d0 tx_buffer_d1 = rx_buffer_d1 tx_buffer_d2 = rx_buffer_d2 tx_buffer_d3 = rx_buffer_d3 tx_buffer_d4 = rx_buffer_d4 tx_buffer_d5 = rx_buffer_d5 CASE cmd_charger_params tx_buffer_cmd = cmd_read_charger_params tx_buffer_addr = my_net_addr[0] tx_buffer_d0 = phase_charge_volts[(charge_phase - 1)] tx_buffer_d1 = phase_charge_amps[(charge_phase -1)] tx_buffer_d2 = charge_time_lo tx_buffer_d3 = charge_time_hi tx_buffer_d4 = pwm_out tx_buffer_d5 = charge_phase CASE cmd_engage_charger REM at some point we really should add a CRC to this and REM the write commands REM REM Maybe LH can suggest one? REM CRCs are not my strong point, I tend to use simple checksums REM if a comparison of the arguments for this and REM for setup_charger fails IF((rx_buffer_d0 <> phase_charge_volts[7]) OR (rx_buffer_d1 <> phase_charge_amps[7]) OR (rx_buffer_d2 <> (write_addr AND 0x00FF)) OR (rx_buffer_d3 <> ((write_addr AND 0xFF00) / 256))) tx_buffer_cmd = cmd_engage_charger_err REM copy the registers so that REM someone debugging can figure out what went wrong tx_buffer_d0 = rx_buffer_d0 tx_buffer_d1 = rx_buffer_d1 tx_buffer_d2 = rx_buffer_d2 tx_buffer_d3 = rx_buffer_d3 tx_buffer_d4 = rx_buffer_d4 tx_buffer_d5 = rx_buffer_d5 ELSE REM otherwise give a OK message and turn the charger on REM note that charge phase of 8, indicating a commanded REM charge tx_buffer_cmd = cmd_engage_charger_ok tx_buffer_d0 = rx_buffer_d0 tx_buffer_d1 = rx_buffer_d1 tx_buffer_d2 = rx_buffer_d2 tx_buffer_d3 = rx_buffer_d3 charge_time_lo = rx_buffer_d2 charge_time_hi = rx_buffer_d3 charge_phase = 8 auto_charge_engaged = lo auto_bal_engaged = lo turn_on_charger() ENDIF CASE cmd_cascading_clear_error errno = 0 tx_buffer_cmd = cmd_cascading_clear_error tx_buffer_addr = my_net_addr[0] + 1 CASE cmd_cascading_prep write_addr = (rx_buffer_d0 * 256) + rx_buffer_d1 write_data = rx_buffer_d2 tx_buffer_addr = my_net_addr[0] + 1 tx_buffer_cmd = cmd_cascading_prep tx_buffer_d0 = rx_buffer_d0 tx_buffer_d1 = rx_buffer_d1 tx_buffer_d2 = rx_buffer_d2 tx_buffer_d3 = rx_buffer_d3 tx_buffer_d4 = sp CASE cmd_cascading_set_baudrate IF(rx_buffer_d0 <> write_data) tx_buffer_cmd = cmd_write_error tx_buffer_d0 = rx_buffer_d0 tx_buffer_d1 = write_data ELSE tx_buffer_cmd = cmd_cascading_set_baudrate baudrate = rx_buffer_d0 tx_buffer_addr = my_net_addr[0] + 1 ENDIF CASE cmd_clear_error IF(rx_buffer_d0 AND errno) tx_buffer_cmd = cmd_write_ok tx_buffer_d1 = errno errno = MIN((errno - rx_buffer_d0),0) tx_buffer_d0 = errno ENDIF CASE cmd_turn_on_led tx_buffer_cmd = cmd_read_ok tx_buffer_d0 = 42 tx_buffer_d1 = 0 turn_on_led() CASE cmd_turn_off_led tx_buffer_cmd = cmd_read_ok tx_buffer_d0 = 42 tx_buffer_d1 = 1 turn_off_led() CASE cmd_manual_charge_control REM NOT TO BE USED EXCEPT FOR CALIBRATION REM THE TIMER DOES NOT SHUT THIS DOWN! REM NOTE THAT config_locked[0] HAS TO BE SET TO THE SPECIAL VALUE 42 REM TO PERFORM THIS OP IF config_locked[0] <> 42 tx_buffer_cmd = cmd_write_error ELSE tx_buffer_cmd = cmd_manual_charge_control_ok current_0 = hi current_1 = hi write_addr = (rx_buffer_d1 * 256) + rx_buffer_d2 pwm((rx_buffer_d0 * 2),write_addr) ENDIF CASE cmd_shutdown_charger turn_off_charger(reason_netcmd) tx_buffer_cmd = cmd_kill_charger_ok CASE cmd_kill_charger shut_down_charger(reason_netcmd_kill) tx_buffer_cmd = cmd_kill_charger_ok tx_buffer_d0 = 1 CASE cmd_reboot reboot : REM a fun one to do broadcast on! ;-) CASE ELSE tx_buffer_cmd = cmd_unknown_cmd tx_buffer_d0 = rx_buffer_cmd ENDSELECT IF (rx_buffer_addr = bcast_addr) REM its a broadcast packet, arm the timer bcast_mode = hi bcast_ok = hi SET TIMER0 TO (bcast_delay * my_net_addr[0]) turn_on_led() ELSE REM its not a broadcast packet, transmit() immediately bcast_mode = lo bcast_ok = lo transmit() ENDIF ENDIF REM broadcast is a special case REM in broadcast mode, we have to wait until timer0 expires REM Normally, this will involve several retries REM unfortunately, this means slowing down the network data REM receive code even more REM but it cant be helped reset_rx() REM NOTE that we leave the receiver disabled during transmit() END SUBROUTINE init_variables() message_size = 0 bcast_mode = 0 bcast_ok = 0 write_addr = 0 write_data = 0 charge_time_hi = 0 charge_time_lo = 0 charge_phase = 0 pwm_out = 0 REM new for 0.06 current_watts = 0 current_temp = (70+40) errno = 0 charged = lo didt_amps = 0 didt_up = 0 REM new for 0.08 IF version[0] <> 8 version[0] = 8 ENDIF stable = hi ain0 = AIN(0) ain1 = AIN(1) ain2 = AIN(2) ain3 = AIN(3) REM end new for 0.06 turn_off_led() turn_off_charger(reason_init) END REM *************** INIT ROUTINES ******************** SUBROUTINE check_sane() IF config_locked[0] <> 69 my_net_addr[0] = 1 pwm_freq[0] = 5000 num_samples[0] = 10 num_vf[0] = 10 dly_vf[0] = 4 div_vf[0] = 1 avg_num[0] = 10 baud[0] = 8 ENDIF IF baud_locked[0] <> 69 baud[0] = 8 ENDIF IF avg_num[0] = 0 avg_num[0] = 10 ENDIF IF my_net_addr[0] = 0 OR my_net_addr[0] = 255 my_net_addr[0] = 1 ENDIF IF pwm_freq[0] < 5 OR pwm_freq[0] > 20000 pwm_freq[0] = 5000 ENDIF IF num_samples[0] = 0 num_samples[0] = 10 ENDIF IF num_vf[0] = 0 num_vf[0] = 10 ENDIF IF dly_vf[0] = 0 dly_vf[0] = 4 ENDIF IF div_vf[0] = 0 div_vf[0] = 1 ENDIF IF(turn_on_balance_phase[0] > 8) turn_on_balance_phase[0] = 0 ENDIF IF(turn_on_charge_phase[0] > 8) turn_on_charge_phase[0] = 0 ENDIF IF(balance_num_batteries_phase[0] > 8) balance_num_batteries_phase[0] = 0 ENDIF END REM ************************** ISRS BEGIN HERE ************************************ VITAL SUBROUTINE errorhandler(err AS INTEGER) last_error[0] = err REM 0.06 no longer transmits any kind of message REM errno will contain the fact that a crash occured REBOOT END VITAL SUBROUTINE comhandler(junk AS INTEGER) local b_inkey as byte REM receive data, place it on the input buffer REM if address is 'D', immediately start a download REM later we may want to preface this with 'only if you are selected and address is D start a dl' REM but for now.. REM note that this routine doesn't execute quite fast enough at 9600 baud REM so we have to slow the other end down a little REM currently in perl I'm doing a select(undef,undef,undef,0.10) which REM in C would be a usleep(100) REM when the data is coming from a vesta basic board, REM this shouldn't be a huge issue but if REM we see a bunch of network timeouts whenever other network boards are talking REM we'll know why ;-( b_inkey = INKEY() REM reset timeout timer IF bcast_mode = 1 bcast_ok = 0 ENDIF SET TIMER0 TO net_timeout IF message_size = 0 AND b_inkey = 'D' IF dl_lock[0] <> 88 PRINT "D" IDE_Download() ENDIF ELSE POKE(begin_rx_buffer + message_size, b_inkey) message_size = message_size + 1 IF (message_size = 8) SET COM1 TO 0 IF (bcast_mode = lo) SET TIMER0 TO 0 ELSE bcast_ok = hi message_size = 0 REM note that this can result in dropped packets while we are REM trying to reply to a broadcast message REM I deem this acceptable as only the main host should be REM sending us command packets and only the main host REM should be sending us broadcast packets REM and the main host ought to be able to know that after sending REM us a broadcast packet, it isn't going to be sending us a REM command packet until we've all answered REM or a timeout has occured REM in any case, we should get out of broadcast REM mode fairly quickly REM and also, it's a bloody battery charger, how much can it possibly matter REM if we can't command it for a extra quarter second SET TIMER0 TO (bcast_delay * my_net_addr[0]) ENDIF ENDIF ENDIF END REM end subroutine comhandler() VITAL SUBROUTINE timerhandler(timernumber AS INTEGER) SELECT timernumber CASE 0 REM serial timeout OR broadcast OK IF((bcast_mode = hi) AND (bcast_ok = hi)) REM ready_to_transmit transmit() bcast_mode = lo bcast_ok = lo SET TIMER0 TO 0 ELSE REM timeout IF beep_on_net_timeout[0] last_message_size[0] = message_size beep() ENDIF message_size = 0 IF (bcast_mode = hi) bcast_ok = hi SET TIMER0 TO (bcast_delay * my_net_addr[0]) ELSE SET TIMER0 TO 0 ENDIF ENDIF CASE 1 REM do something REM I plan to use Timer1 for something, not sure what just yet ENDSELECT END REM end subroutine timerhandler() REM *****************************MAIN BEGINS HERE********************************** REM **************** INITILIZATON **************** PIPE PRINT COMM1 PIPE INPUT COMM1 init_variables() check_sane() REM setup baudrate baudrate = baud[0] ON COM1 comhandler ON TIMER0 timerhandler ON ERROR errorhandler SET COM1 TO 1 SET TIMER0 TO 0 POKE(0x85,PEEK(0x85) OR 0x0f) : REM prepare the A/D POKE(0x9f,2) HIBERNATE(1) : REM to facilitate downloads DO WHILE 1 IF(message_size = 8) handle_network() ENDIF REM see if it has been 1 second since our last event proc IF(message_size = 0) poll_inputs() ENDIF IF PEEK(40) <> last_sec last_sec = PEEK(40) REM it has been one second REM so let's poll the A/D and store the results one_second_charger_stuff() SELECT last_sec CASE 15 check_for_errors() CASE 30 check_for_errors() CASE 58 check_for_errors() CASE 45 check_for_errors() sixty_second_charger_stuff() timeout = timeout + 1 IF timeout_minutes[0] AND (timeout = timeout_minutes[0]) AND sleep_seconds[0] HIBERNATE(sleep_seconds[0]) ENDIF ENDSELECT IF charge_inhibited() shut_down_charger(reason_charge_error_inhibited) ENDIF ENDIF LOOP